@nestia/core 2.0.0-dev.20230831-5 → 2.0.0-dev.20230991

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 (62) hide show
  1. package/lib/decorators/EncryptedRoute.js +10 -7
  2. package/lib/decorators/EncryptedRoute.js.map +1 -1
  3. package/lib/decorators/TypedHeaders.js +2 -2
  4. package/lib/decorators/TypedHeaders.js.map +1 -1
  5. package/lib/decorators/TypedParam.d.ts +14 -13
  6. package/lib/decorators/TypedParam.js +16 -48
  7. package/lib/decorators/TypedParam.js.map +1 -1
  8. package/lib/decorators/TypedQuery.d.ts +2 -3
  9. package/lib/decorators/TypedQuery.js +4 -5
  10. package/lib/decorators/TypedQuery.js.map +1 -1
  11. package/lib/decorators/TypedRoute.js +8 -5
  12. package/lib/decorators/TypedRoute.js.map +1 -1
  13. package/lib/decorators/internal/{TransformError.js → NoTransformConfigureError.js} +4 -4
  14. package/lib/decorators/internal/NoTransformConfigureError.js.map +1 -0
  15. package/lib/decorators/internal/get_path_and_stringify.js +2 -2
  16. package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
  17. package/lib/decorators/internal/validate_request_body.js +2 -2
  18. package/lib/decorators/internal/validate_request_body.js.map +1 -1
  19. package/lib/programmers/PlainBodyProgrammer.js +17 -13
  20. package/lib/programmers/PlainBodyProgrammer.js.map +1 -1
  21. package/lib/programmers/TypedHeadersProgrammer.d.ts +3 -0
  22. package/lib/programmers/TypedHeadersProgrammer.js +86 -136
  23. package/lib/programmers/TypedHeadersProgrammer.js.map +1 -1
  24. package/lib/programmers/TypedParamProgrammer.d.ts +3 -1
  25. package/lib/programmers/TypedParamProgrammer.js +74 -67
  26. package/lib/programmers/TypedParamProgrammer.js.map +1 -1
  27. package/lib/programmers/TypedQueryProgrammer.d.ts +3 -0
  28. package/lib/programmers/TypedQueryProgrammer.js +88 -149
  29. package/lib/programmers/TypedQueryProgrammer.js.map +1 -1
  30. package/lib/programmers/TypedRouteProgrammer.js +8 -8
  31. package/lib/programmers/TypedRouteProgrammer.js.map +1 -1
  32. package/lib/programmers/internal/CoreMetadataUtil.d.ts +5 -0
  33. package/lib/programmers/internal/CoreMetadataUtil.js +44 -0
  34. package/lib/programmers/internal/CoreMetadataUtil.js.map +1 -0
  35. package/lib/transform.d.ts +2 -1
  36. package/lib/transform.js +11 -3
  37. package/lib/transform.js.map +1 -1
  38. package/lib/transformers/FileTransformer.d.ts +1 -1
  39. package/lib/transformers/FileTransformer.js +16 -1
  40. package/lib/transformers/FileTransformer.js.map +1 -1
  41. package/lib/transformers/ParameterDecoratorTransformer.js +3 -1
  42. package/lib/transformers/ParameterDecoratorTransformer.js.map +1 -1
  43. package/package.json +5 -5
  44. package/src/decorators/EncryptedRoute.ts +9 -12
  45. package/src/decorators/TypedHeaders.ts +2 -2
  46. package/src/decorators/TypedParam.ts +30 -51
  47. package/src/decorators/TypedQuery.ts +4 -5
  48. package/src/decorators/TypedRoute.ts +5 -10
  49. package/src/decorators/internal/{TransformError.ts → NoTransformConfigureError.ts} +1 -1
  50. package/src/decorators/internal/get_path_and_stringify.ts +2 -2
  51. package/src/decorators/internal/validate_request_body.ts +2 -2
  52. package/src/programmers/PlainBodyProgrammer.ts +24 -19
  53. package/src/programmers/TypedHeadersProgrammer.ts +100 -106
  54. package/src/programmers/TypedParamProgrammer.ts +113 -79
  55. package/src/programmers/TypedQueryProgrammer.ts +115 -114
  56. package/src/programmers/TypedRouteProgrammer.ts +11 -8
  57. package/src/programmers/internal/CoreMetadataUtil.ts +21 -0
  58. package/src/transform.ts +14 -5
  59. package/src/transformers/FileTransformer.ts +7 -2
  60. package/src/transformers/ParameterDecoratorTransformer.ts +2 -1
  61. package/lib/decorators/internal/TransformError.js.map +0 -1
  62. /package/lib/decorators/internal/{TransformError.d.ts → NoTransformConfigureError.d.ts} +0 -0
@@ -16,17 +16,12 @@ import { HttpArgumentsHost } from "@nestjs/common/interfaces";
16
16
  import express from "express";
17
17
  import { catchError, map } from "rxjs/operators";
18
18
 
19
- import {
20
- assertStringify,
21
- isStringify,
22
- stringify,
23
- validateStringify,
24
- } from "typia";
19
+ import typia from "typia";
25
20
 
26
21
  import { IResponseBodyStringifier } from "../options/IResponseBodyStringifier";
27
22
  import { Singleton } from "../utils/Singleton";
28
23
  import { ENCRYPTION_METADATA_KEY } from "./internal/EncryptedConstant";
29
- import { TransformError } from "./internal/TransformError";
24
+ import { NoTransformConfigureError } from "./internal/NoTransformConfigureError";
30
25
  import { get_path_and_stringify } from "./internal/get_path_and_stringify";
31
26
  import { headers_to_object } from "./internal/headers_to_object";
32
27
  import { route_error } from "./internal/route_error";
@@ -118,10 +113,10 @@ export namespace EncryptedRoute {
118
113
  }
119
114
 
120
115
  for (const method of [
121
- isStringify,
122
- assertStringify,
123
- validateStringify,
124
- stringify,
116
+ typia.json.isStringify,
117
+ typia.json.assertStringify,
118
+ typia.json.validateStringify,
119
+ typia.json.stringify,
125
120
  ])
126
121
  for (const [key, value] of Object.entries(method))
127
122
  for (const deco of [
@@ -154,7 +149,9 @@ class EncryptedRouteInterceptor implements NestInterceptor {
154
149
  context.getClass(),
155
150
  );
156
151
  if (!param)
157
- throw TransformError(`EncryptedRoute.${this.method}`);
152
+ throw NoTransformConfigureError(
153
+ `EncryptedRoute.${this.method}`,
154
+ );
158
155
 
159
156
  const headers: Singleton<Record<string, string>> =
160
157
  new Singleton(() => {
@@ -8,7 +8,7 @@ import type { FastifyRequest } from "fastify";
8
8
 
9
9
  import typia, { TypeGuardError, assert } from "typia";
10
10
 
11
- import { TransformError } from "./internal/TransformError";
11
+ import { NoTransformConfigureError } from "./internal/NoTransformConfigureError";
12
12
 
13
13
  /**
14
14
  * Type safe HTTP headers decorator.
@@ -54,7 +54,7 @@ import { TransformError } from "./internal/TransformError";
54
54
  export function TypedHeaders<T extends object>(
55
55
  decoder?: (headers: Record<string, string | string[] | undefined>) => T,
56
56
  ): ParameterDecorator {
57
- if (decoder === undefined) throw TransformError("TypedHeaders");
57
+ if (decoder === undefined) throw NoTransformConfigureError("TypedHeaders");
58
58
 
59
59
  return createParamDecorator(function TypedHeaders(
60
60
  _unknown: any,
@@ -6,81 +6,60 @@ import {
6
6
  import type express from "express";
7
7
  import type { FastifyRequest } from "fastify";
8
8
 
9
+ import { NoTransformConfigureError } from "./internal/NoTransformConfigureError";
10
+
9
11
  /**
10
12
  * Type safe URL parameter decorator.
11
13
  *
12
14
  * `TypedParam` is a decorator function getting specific typed parameter from the
13
15
  * HTTP request URL. It's almost same with the {@link nest.Param}, but `TypedParam`
14
- * automatically casts parameter value to be following its type. Beside, the
15
- * {@link nest.Param} always parses all of the parameters as `string` type.
16
- *
17
- * Note that, if you just omit the `type` and `nullable` parameters, then
18
- * `TypedParam` automatically determines the `type` and `nullable` values
19
- * just by analyzing the parameter type. Only when you need to specify them
20
- * are, you want to use the `uuid` type.
16
+ * automatically casts parameter value to be following its type, and validates it.
21
17
  *
22
18
  * ```typescript
19
+ * import { tags } from "typia";
20
+ *
23
21
  * \@TypedRoute.Get("shopping/sales/:id/:no/:paused")
24
22
  * public async pause
25
23
  * (
26
- * \@TypedParam("id", "uuid"), id: string, // uuid specification
27
- * \@TypedParam("no") id: number, // auto casting
28
- * \@TypedParam("paused") paused: boolean | null // auto casting
24
+ * \@TypedParam("id", "uuid"), id: string & tags.Format<"uuid">,
25
+ * \@TypedParam("no") id: number & tags.Type<"uint32">
26
+ * \@TypedParam("paused") paused: boolean | null
29
27
  * ): Promise<void>;
30
28
  * ```
31
29
  *
32
30
  * @param name URL Parameter name
33
- * @param type If omit, automatically determined by the parameter type.
34
- * @param nullable If omit, automatically determined by the parameter type.
35
31
  * @returns Parameter decorator
36
32
  *
37
33
  * @author Jeongho Nam - https://github.com/samchon
38
34
  */
39
35
  export function TypedParam(
40
36
  name: string,
41
- type?: "boolean" | "number" | "string" | "uuid" | "date",
42
- nullable?: false | true,
37
+ props?: TypedParam.IProps<any>,
43
38
  ): ParameterDecorator {
44
- function TypedParam({}: any, context: ExecutionContext) {
39
+ if (props === undefined) throw NoTransformConfigureError("TypedParam");
40
+
41
+ return createParamDecorator(function TypedParam(
42
+ {}: any,
43
+ context: ExecutionContext,
44
+ ) {
45
45
  const request: express.Request | FastifyRequest = context
46
46
  .switchToHttp()
47
47
  .getRequest();
48
48
  const str: string = (request.params as any)[name];
49
-
50
- if (nullable === true && str === "null") return null;
51
- else if (type === "boolean") {
52
- if (str === "true" || str === "1") return true;
53
- else if (str === "false" || str === "0") return false;
54
- else
55
- throw new BadRequestException(
56
- `Value of the URL parameter '${name}' is not a boolean.`,
57
- );
58
- } else if (type === "number") {
59
- const value: number = Number(str);
60
- if (isNaN(value))
61
- throw new BadRequestException(
62
- `Value of the URL parameter "${name}" is not a number.`,
63
- );
64
- return value;
65
- } else if (type === "uuid") {
66
- if (UUID_PATTERN.test(str) === false)
67
- throw new BadRequestException(
68
- `Value of the URL parameter "${name}" is not a valid UUID.`,
69
- );
70
- return str;
71
- } else if (type === "date") {
72
- if (DATE_PATTERN.test(str) === false)
73
- throw new BadRequestException(
74
- `Value of the URL parameter "${name}" is not a valid date.`,
75
- );
76
- return str;
77
- } else return str;
49
+ const value: any = props!.cast(str);
50
+ if (props!.is(value) === false)
51
+ throw new BadRequestException(
52
+ `Value of the URL parameter "${name}" is not ${
53
+ props!.type
54
+ } type.`,
55
+ );
56
+ return value;
57
+ })(name);
58
+ }
59
+ export namespace TypedParam {
60
+ export interface IProps<T> {
61
+ type: string;
62
+ cast: (value: string) => T;
63
+ is: (value: T) => boolean;
78
64
  }
79
- (TypedParam as any).nullable = !!nullable;
80
- (TypedParam as any).type = type;
81
- return createParamDecorator(TypedParam)(name);
82
65
  }
83
-
84
- const UUID_PATTERN =
85
- /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
86
- const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
@@ -8,7 +8,7 @@ import type { FastifyRequest } from "fastify";
8
8
 
9
9
  import typia, { TypeGuardError, assert } from "typia";
10
10
 
11
- import { TransformError } from "./internal/TransformError";
11
+ import { NoTransformConfigureError } from "./internal/NoTransformConfigureError";
12
12
 
13
13
  /**
14
14
  * Type safe URL query decorator.
@@ -23,9 +23,8 @@ import { TransformError } from "./internal/TransformError";
23
23
  *
24
24
  * 1. Type `T` must be an object type
25
25
  * 2. Do not allow dynamic property
26
- * 3. Prpoerty value cannot be `undefined`, but `null` is possible
27
- * 4. Only `boolean`, `bigint`, `number`, `string` or their array types are allowed
28
- * 5. By the way, union type never be not allowed
26
+ * 3. Only `boolean`, `bigint`, `number`, `string` or their array types are allowed
27
+ * 4. By the way, union type never be not allowed
29
28
  *
30
29
  * @returns Parameter decorator
31
30
  * @author Jeongho Nam - https://github.com/samchon
@@ -33,7 +32,7 @@ import { TransformError } from "./internal/TransformError";
33
32
  export function TypedQuery<T extends object>(
34
33
  decoder?: (params: URLSearchParams) => T,
35
34
  ): ParameterDecorator {
36
- if (decoder === undefined) throw TransformError("TypedQuery");
35
+ if (decoder === undefined) throw NoTransformConfigureError("TypedQuery");
37
36
 
38
37
  return createParamDecorator(function TypedQuery(
39
38
  _unknown: any,
@@ -14,12 +14,7 @@ import { HttpArgumentsHost } from "@nestjs/common/interfaces";
14
14
  import express from "express";
15
15
  import { catchError, map } from "rxjs/operators";
16
16
 
17
- import {
18
- assertStringify,
19
- isStringify,
20
- stringify,
21
- validateStringify,
22
- } from "typia";
17
+ import typia from "typia";
23
18
 
24
19
  import { IResponseBodyStringifier } from "../options/IResponseBodyStringifier";
25
20
  import { get_path_and_stringify } from "./internal/get_path_and_stringify";
@@ -107,10 +102,10 @@ export namespace TypedRoute {
107
102
  }
108
103
  }
109
104
  for (const method of [
110
- stringify,
111
- isStringify,
112
- assertStringify,
113
- validateStringify,
105
+ typia.json.stringify,
106
+ typia.json.isStringify,
107
+ typia.json.assertStringify,
108
+ typia.json.validateStringify,
114
109
  ])
115
110
  for (const [key, value] of Object.entries(method))
116
111
  for (const deco of [
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @internal
3
3
  */
4
- export function TransformError(method: string): Error {
4
+ export function NoTransformConfigureError(method: string): Error {
5
5
  return new Error(
6
6
  `Error on nestia.core.${method}(): no transform has been configured. Run "npx typia setup" command, or check if you're using non-standard TypeScript compiler like Babel or SWC.`,
7
7
  );
@@ -3,7 +3,7 @@ import { InternalServerErrorException } from "@nestjs/common";
3
3
  import typia, { IValidation, TypeGuardError } from "typia";
4
4
 
5
5
  import { IResponseBodyStringifier } from "../../options/IResponseBodyStringifier";
6
- import { TransformError } from "./TransformError";
6
+ import { NoTransformConfigureError } from "./NoTransformConfigureError";
7
7
 
8
8
  export const get_path_and_stringify =
9
9
  (method: string) =>
@@ -24,7 +24,7 @@ export const get_path_and_stringify =
24
24
  const take =
25
25
  (method: string) =>
26
26
  <T>(functor?: IResponseBodyStringifier<T> | null) => {
27
- if (functor === undefined) throw TransformError(method);
27
+ if (functor === undefined) throw NoTransformConfigureError(method);
28
28
  else if (functor === null) return JSON.stringify;
29
29
  else if (functor.type === "stringify") return functor.stringify;
30
30
  else if (functor.type === "assert") return assert(functor.assert);
@@ -3,12 +3,12 @@ import { BadRequestException } from "@nestjs/common";
3
3
  import typia, { IValidation, TypeGuardError } from "typia";
4
4
 
5
5
  import { IRequestBodyValidator } from "../../options/IRequestBodyValidator";
6
- import { TransformError } from "./TransformError";
6
+ import { NoTransformConfigureError } from "./NoTransformConfigureError";
7
7
 
8
8
  export const validate_request_body =
9
9
  (method: string) =>
10
10
  <T>(validator?: IRequestBodyValidator<T>) => {
11
- if (!validator) return () => TransformError(method);
11
+ if (!validator) return () => NoTransformConfigureError(method);
12
12
  else if (validator.type === "assert") return assert(validator.assert);
13
13
  else if (validator.type === "is") return is(validator.is);
14
14
  else if (validator.type === "validate")
@@ -2,8 +2,9 @@ import ts from "typescript";
2
2
 
3
3
  import { MetadataCollection } from "typia/lib/factories/MetadataCollection";
4
4
  import { MetadataFactory } from "typia/lib/factories/MetadataFactory";
5
- import { Metadata } from "typia/lib/metadata/Metadata";
6
5
  import { AssertProgrammer } from "typia/lib/programmers/AssertProgrammer";
6
+ import { Metadata } from "typia/lib/schemas/metadata/Metadata";
7
+ import { TransformerError } from "typia/lib/transformers/TransformerError";
7
8
 
8
9
  import { INestiaTransformProject } from "../options/INestiaTransformProject";
9
10
 
@@ -12,13 +13,16 @@ export namespace PlainBodyProgrammer {
12
13
  (project: INestiaTransformProject) =>
13
14
  (modulo: ts.LeftHandSideExpression) =>
14
15
  (type: ts.Type): readonly ts.Expression[] => {
15
- validate(
16
- MetadataFactory.analyze(project.checker)({
17
- resolve: false,
18
- constant: true,
19
- absorb: true,
20
- })(new MetadataCollection())(type),
21
- );
16
+ const result = MetadataFactory.analyze(project.checker)({
17
+ escape: false,
18
+ constant: true,
19
+ absorb: true,
20
+ validate,
21
+ })(new MetadataCollection())(type);
22
+ if (result.success === false)
23
+ throw TransformerError.from("@core.nestia.TypedParam")(
24
+ result.errors,
25
+ );
22
26
  return [
23
27
  AssertProgrammer.write({
24
28
  ...project,
@@ -32,22 +36,23 @@ export namespace PlainBodyProgrammer {
32
36
  };
33
37
  }
34
38
 
35
- const validate = (metadata: Metadata): void => {
39
+ const validate = (metadata: Metadata): string[] => {
40
+ const errors: string[] = [];
41
+ const insert = (msg: string) => errors.push(msg);
42
+
36
43
  const expected: number =
37
- (metadata.atomics.some((t) => t === "string") ? 1 : 0) +
44
+ (metadata.atomics.some((a) => a.type === "string") ? 1 : 0) +
38
45
  metadata.templates.length +
39
46
  metadata.constants
40
47
  .filter((c) => c.type === "string")
41
48
  .map((c) => c.values.length)
42
49
  .reduce((a, b) => a + b, 0);
43
50
  if (expected === 0 || expected !== metadata.size())
44
- throw error(`only string type is allowed`);
45
- else if (metadata.isRequired() === false)
46
- throw error(`do not allow undefindable type`);
47
- else if (metadata.nullable === true)
48
- throw error(`do not allow nullable type`);
49
- else if (metadata.any === true) throw error(`do not allow any type`);
50
- };
51
+ insert(`only string type is allowed`);
52
+ if (metadata.isRequired() === false)
53
+ insert(`do not allow undefindable type`);
54
+ if (metadata.nullable === true) insert(`do not allow nullable type`);
55
+ else if (metadata.any === true) insert(`do not allow any type`);
51
56
 
52
- const error = (msg: string) =>
53
- new Error(`Error on nestia.core.PlainBody(): ${msg}.`);
57
+ return errors;
58
+ };
@@ -4,15 +4,18 @@ import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
4
4
  import { MetadataCollection } from "typia/lib/factories/MetadataCollection";
5
5
  import { MetadataFactory } from "typia/lib/factories/MetadataFactory";
6
6
  import { StatementFactory } from "typia/lib/factories/StatementFactory";
7
- import { Metadata } from "typia/lib/metadata/Metadata";
8
- import { MetadataObject } from "typia/lib/metadata/MetadataObject";
9
- import { MetadataProperty } from "typia/lib/metadata/MetadataProperty";
10
7
  import { AssertProgrammer } from "typia/lib/programmers/AssertProgrammer";
11
8
  import { FunctionImporter } from "typia/lib/programmers/helpers/FunctionImporeter";
9
+ import { Metadata } from "typia/lib/schemas/metadata/Metadata";
10
+ import { MetadataArray } from "typia/lib/schemas/metadata/MetadataArray";
11
+ import { MetadataObject } from "typia/lib/schemas/metadata/MetadataObject";
12
+ import { MetadataProperty } from "typia/lib/schemas/metadata/MetadataProperty";
13
+ import { TransformerError } from "typia/lib/transformers/TransformerError";
12
14
  import { Atomic } from "typia/lib/typings/Atomic";
13
15
  import { Escaper } from "typia/lib/utils/Escaper";
14
16
 
15
17
  import { INestiaTransformProject } from "../options/INestiaTransformProject";
18
+ import { CoreMetadataUtil } from "./internal/CoreMetadataUtil";
16
19
 
17
20
  export namespace TypedHeadersProgrammer {
18
21
  export const generate =
@@ -23,113 +26,101 @@ export namespace TypedHeadersProgrammer {
23
26
  return decode(project, modulo)(type, object);
24
27
  };
25
28
 
29
+ export const validate = (
30
+ meta: Metadata,
31
+ explore: MetadataFactory.IExplore,
32
+ ): string[] => {
33
+ const errors: string[] = [];
34
+ const insert = (msg: string) => errors.push(msg);
35
+
36
+ if (explore.top === true) {
37
+ // TOP MUST BE ONLY OBJECT
38
+ if (meta.objects.length !== 1 || meta.bucket() !== 1)
39
+ insert("only one object type is allowed.");
40
+ if (meta.nullable === true) insert("headers cannot be null.");
41
+ if (meta.isRequired() === false) insert("headers cannot be null.");
42
+ } else if (
43
+ explore.nested !== null &&
44
+ explore.nested instanceof MetadataArray
45
+ ) {
46
+ const atomics = CoreMetadataUtil.atomics(meta);
47
+ const expected: number =
48
+ meta.atomics.length +
49
+ meta.templates.length +
50
+ meta.constants
51
+ .map((c) => c.values.length)
52
+ .reduce((a, b) => a + b, 0);
53
+ if (atomics.size > 1) insert("union type is not allowed in array.");
54
+ if (meta.nullable) insert("nullable type is not allowed in array.");
55
+ if (meta.isRequired() === false)
56
+ insert("optional type is not allowed.");
57
+ if (meta.size() !== expected)
58
+ insert("only atomic or constant types are allowed in array.");
59
+ } else if (explore.object && explore.property !== null) {
60
+ //----
61
+ // COMMON
62
+ //----
63
+ // PROPERTY MUST BE SOLE
64
+ if (typeof explore.property === "object")
65
+ insert("dynamic property is not allowed.");
66
+ // MUST BE LOWER-CASE
67
+ if (
68
+ typeof explore.property === "string" &&
69
+ explore.property !== explore.property.toLowerCase()
70
+ )
71
+ insert("property name must be lower-case.");
72
+ // DO NOT ALLOW TUPLE TYPE
73
+ if (meta.tuples.length) insert("tuple type is not allowed.");
74
+ // DO NOT ALLOW UNION TYPE
75
+ if (CoreMetadataUtil.isUnion(meta))
76
+ insert("union type is not allowed.");
77
+ // DO NOT ALLOW NESTED OBJECT
78
+ if (
79
+ meta.objects.length ||
80
+ meta.sets.length ||
81
+ meta.maps.length ||
82
+ meta.natives.length
83
+ )
84
+ insert("nested object type is not allowed.");
85
+ // DO NOT ALLOW NULLABLE
86
+ if (meta.nullable) insert("nullable type is not allowed.");
87
+
88
+ //----
89
+ // ARRAY CASES
90
+ //----
91
+ const isArray: boolean = meta.arrays.length > 1;
92
+ // ARRAY TYPE MUST BE REQUIRED
93
+ if (isArray && meta.isRequired() === false)
94
+ insert("optional type is not allowed when array.");
95
+ // SET-COOKIE MUST BE ARRAY
96
+ if (explore.property === "set-cookie" && !isArray)
97
+ insert("set-cookie property must be array.");
98
+ // MUST BE SINGULAR CASE
99
+ if (
100
+ typeof explore.property === "string" &&
101
+ SINGULAR.has(explore.property) &&
102
+ isArray
103
+ )
104
+ insert("property cannot be array.");
105
+ }
106
+ return errors;
107
+ };
108
+
26
109
  const getObject =
27
110
  (checker: ts.TypeChecker) =>
28
111
  (type: ts.Type): MetadataObject => {
29
112
  const collection: MetadataCollection = new MetadataCollection();
30
- const metadata: Metadata = MetadataFactory.analyze(checker)({
31
- resolve: false,
113
+ const result = MetadataFactory.analyze(checker)({
114
+ escape: false,
32
115
  constant: true,
33
116
  absorb: true,
117
+ validate,
34
118
  })(collection)(type);
35
- if (metadata.objects.length !== 1 || metadata.bucket() !== 1)
36
- throw new Error(
37
- ErrorMessages.object(metadata)(
38
- "only one object type is allowed.",
39
- ),
40
- );
41
- else if (metadata.nullable === true)
42
- throw new Error(
43
- ErrorMessages.object(metadata)(
44
- "query parameter cannot be null.",
45
- ),
46
- );
47
- else if (metadata.isRequired() === false)
48
- throw new Error(
49
- ErrorMessages.object(metadata)(
50
- "query parameter cannot be undefined.",
51
- ),
52
- );
53
-
54
- const object: MetadataObject = metadata.objects[0]!;
55
- if (object.properties.some((p) => !(p.key as any).isSoleLiteral()))
56
- throw new Error(
57
- ErrorMessages.object(metadata)(
58
- "dynamic property is not allowed.",
59
- ),
60
- );
61
-
62
- for (const property of object.properties) {
63
- const key: string = property.key.constants[0]
64
- .values[0] as string;
65
- const value: Metadata = property.value;
66
- validate(object)(key)(value, 0);
67
- }
68
- return object;
69
- };
70
-
71
- const validate =
72
- (obj: MetadataObject) =>
73
- (key: string) =>
74
- (value: Metadata, depth: number): string[] => {
75
- if (depth === 1 && value.isRequired() === false)
76
- throw new Error(
77
- ErrorMessages.property(obj)(key)(
78
- "optional type is not allowed in array.",
79
- ),
80
- );
81
- else if (value.nullable === true)
82
- throw new Error(
83
- ErrorMessages.property(obj)(key)(
84
- "nullable type is not allowed.",
85
- ),
119
+ if (result.success === false)
120
+ throw TransformerError.from("@core.nestia.TypedHeaders")(
121
+ result.errors,
86
122
  );
87
- else if (
88
- value.maps.length ||
89
- value.sets.length ||
90
- value.objects.length
91
- )
92
- throw new Error(
93
- ErrorMessages.property(obj)(key)(
94
- "object type is not allowed",
95
- ),
96
- );
97
-
98
- const atom: string[] = [];
99
- for (const type of value.atomics) atom.push(type);
100
- for (const { type } of value.constants) atom.push(type);
101
-
102
- if (depth === 0 && (value.arrays.length || value.arrays.length)) {
103
- if (atom.length)
104
- throw new Error(
105
- ErrorMessages.property(obj)(key)(
106
- "union type is not allowed",
107
- ),
108
- );
109
- for (const array of value.arrays)
110
- atom.push(...validate(obj)(key)(array.value, depth + 1));
111
- for (const tuple of value.tuples)
112
- for (const elem of tuple.elements)
113
- atom.push(...validate(obj)(key)(elem, depth + 1));
114
- } else if (value.arrays.length || value.tuples.length)
115
- throw new Error(
116
- ErrorMessages.property(obj)(key)(
117
- "double-array type is not allowed",
118
- ),
119
- );
120
-
121
- const size: number = new Set(atom).size;
122
- if (size === 0)
123
- throw new Error(
124
- ErrorMessages.property(obj)(key)("unknown type"),
125
- );
126
- else if (size > 1)
127
- throw new Error(
128
- ErrorMessages.property(obj)(key)(
129
- "union type is not allowed",
130
- ),
131
- );
132
- return atom;
123
+ return result.data.objects[0]!;
133
124
  };
134
125
 
135
126
  const decode =
@@ -156,7 +147,9 @@ export namespace TypedHeadersProgrammer {
156
147
  })(modulo)(false)(type);
157
148
  const output: ts.Identifier = ts.factory.createIdentifier("output");
158
149
 
159
- const importer: FunctionImporter = new FunctionImporter();
150
+ const importer: FunctionImporter = new FunctionImporter(
151
+ "TypedHeaders",
152
+ );
160
153
  const statements: ts.Statement[] = [
161
154
  StatementFactory.constant(
162
155
  "output",
@@ -189,14 +182,15 @@ export namespace TypedHeadersProgrammer {
189
182
 
190
183
  const [type, isArray]: [Atomic.Literal, boolean] = value.atomics
191
184
  .length
192
- ? [value.atomics[0], false]
185
+ ? [value.atomics[0].type, false]
193
186
  : value.constants.length
194
187
  ? [value.constants[0]!.type, false]
195
188
  : (() => {
196
189
  const meta =
197
- value.arrays[0]?.value ?? value.tuples[0].elements[0];
190
+ value.arrays[0]?.type.value ??
191
+ value.tuples[0].type.elements[0];
198
192
  return meta.atomics.length
199
- ? [meta.atomics[0], true]
193
+ ? [meta.atomics[0].type, true]
200
194
  : [meta.constants[0]!.type, true];
201
195
  })();
202
196
  if (isArray && SINGULAR.has(key))