@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.
- package/lib/decorators/EncryptedRoute.js +10 -7
- package/lib/decorators/EncryptedRoute.js.map +1 -1
- package/lib/decorators/TypedHeaders.js +2 -2
- package/lib/decorators/TypedHeaders.js.map +1 -1
- package/lib/decorators/TypedParam.d.ts +14 -13
- package/lib/decorators/TypedParam.js +16 -48
- package/lib/decorators/TypedParam.js.map +1 -1
- package/lib/decorators/TypedQuery.d.ts +2 -3
- package/lib/decorators/TypedQuery.js +4 -5
- package/lib/decorators/TypedQuery.js.map +1 -1
- package/lib/decorators/TypedRoute.js +8 -5
- package/lib/decorators/TypedRoute.js.map +1 -1
- package/lib/decorators/internal/{TransformError.js → NoTransformConfigureError.js} +4 -4
- package/lib/decorators/internal/NoTransformConfigureError.js.map +1 -0
- package/lib/decorators/internal/get_path_and_stringify.js +2 -2
- package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
- package/lib/decorators/internal/validate_request_body.js +2 -2
- package/lib/decorators/internal/validate_request_body.js.map +1 -1
- package/lib/programmers/PlainBodyProgrammer.js +17 -13
- package/lib/programmers/PlainBodyProgrammer.js.map +1 -1
- package/lib/programmers/TypedHeadersProgrammer.d.ts +3 -0
- package/lib/programmers/TypedHeadersProgrammer.js +86 -136
- package/lib/programmers/TypedHeadersProgrammer.js.map +1 -1
- package/lib/programmers/TypedParamProgrammer.d.ts +3 -1
- package/lib/programmers/TypedParamProgrammer.js +74 -67
- package/lib/programmers/TypedParamProgrammer.js.map +1 -1
- package/lib/programmers/TypedQueryProgrammer.d.ts +3 -0
- package/lib/programmers/TypedQueryProgrammer.js +88 -149
- package/lib/programmers/TypedQueryProgrammer.js.map +1 -1
- package/lib/programmers/TypedRouteProgrammer.js +8 -8
- package/lib/programmers/TypedRouteProgrammer.js.map +1 -1
- package/lib/programmers/internal/CoreMetadataUtil.d.ts +5 -0
- package/lib/programmers/internal/CoreMetadataUtil.js +44 -0
- package/lib/programmers/internal/CoreMetadataUtil.js.map +1 -0
- package/lib/transform.d.ts +2 -1
- package/lib/transform.js +11 -3
- package/lib/transform.js.map +1 -1
- package/lib/transformers/FileTransformer.d.ts +1 -1
- package/lib/transformers/FileTransformer.js +16 -1
- package/lib/transformers/FileTransformer.js.map +1 -1
- package/lib/transformers/ParameterDecoratorTransformer.js +3 -1
- package/lib/transformers/ParameterDecoratorTransformer.js.map +1 -1
- package/package.json +5 -5
- package/src/decorators/EncryptedRoute.ts +9 -12
- package/src/decorators/TypedHeaders.ts +2 -2
- package/src/decorators/TypedParam.ts +30 -51
- package/src/decorators/TypedQuery.ts +4 -5
- package/src/decorators/TypedRoute.ts +5 -10
- package/src/decorators/internal/{TransformError.ts → NoTransformConfigureError.ts} +1 -1
- package/src/decorators/internal/get_path_and_stringify.ts +2 -2
- package/src/decorators/internal/validate_request_body.ts +2 -2
- package/src/programmers/PlainBodyProgrammer.ts +24 -19
- package/src/programmers/TypedHeadersProgrammer.ts +100 -106
- package/src/programmers/TypedParamProgrammer.ts +113 -79
- package/src/programmers/TypedQueryProgrammer.ts +115 -114
- package/src/programmers/TypedRouteProgrammer.ts +11 -8
- package/src/programmers/internal/CoreMetadataUtil.ts +21 -0
- package/src/transform.ts +14 -5
- package/src/transformers/FileTransformer.ts +7 -2
- package/src/transformers/ParameterDecoratorTransformer.ts +2 -1
- package/lib/decorators/internal/TransformError.js.map +0 -1
- /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 {
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
27
|
-
* \@TypedParam("no") id: number
|
|
28
|
-
* \@TypedParam("paused") paused: boolean | null
|
|
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
|
-
|
|
42
|
-
nullable?: false | true,
|
|
37
|
+
props?: TypedParam.IProps<any>,
|
|
43
38
|
): ParameterDecorator {
|
|
44
|
-
|
|
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 (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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 {
|
|
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.
|
|
27
|
-
* 4.
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
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 {
|
|
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 () =>
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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):
|
|
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((
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
|
31
|
-
|
|
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 (
|
|
36
|
-
throw
|
|
37
|
-
|
|
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
|
-
|
|
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 ??
|
|
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))
|