@nestia/sdk 13.0.4 → 14.0.1
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/README.md +1 -1
- package/assets/bundle/distribute/package.json +2 -2
- package/assets/bundle/distribute/tsconfig.json +15 -105
- package/lib/INestiaConfig.d.ts +19 -10
- package/lib/NestiaSdkApplication.js +58 -27
- package/lib/NestiaSdkApplication.js.map +1 -1
- package/lib/NestiaSwaggerComposer.js +11 -10
- package/lib/NestiaSwaggerComposer.js.map +1 -1
- package/lib/analyses/ConfigAnalyzer.js +4 -4
- package/lib/analyses/ConfigAnalyzer.js.map +1 -1
- package/lib/analyses/ParameterNameAnalyzer.d.ts +20 -0
- package/lib/analyses/ParameterNameAnalyzer.js +48 -0
- package/lib/analyses/ParameterNameAnalyzer.js.map +1 -0
- package/lib/analyses/PathAnalyzer.d.ts +30 -0
- package/lib/analyses/PathAnalyzer.js +108 -16
- package/lib/analyses/PathAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectControllerAnalyzer.js +2 -1
- package/lib/analyses/ReflectControllerAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationAnalyzer.js +7 -7
- package/lib/analyses/ReflectHttpOperationAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationParameterAnalyzer.js +122 -23
- package/lib/analyses/ReflectHttpOperationParameterAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js +5 -2
- package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectMcpOperationAnalyzer.js +5 -2
- package/lib/analyses/ReflectMcpOperationAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectWebSocketOperationAnalyzer.js +6 -3
- package/lib/analyses/ReflectWebSocketOperationAnalyzer.js.map +1 -1
- package/lib/analyses/SecurityAnalyzer.d.ts +10 -0
- package/lib/analyses/SecurityAnalyzer.js +24 -19
- package/lib/analyses/SecurityAnalyzer.js.map +1 -1
- package/lib/analyses/SwaggerExampleAnalyzer.d.ts +15 -0
- package/lib/analyses/SwaggerExampleAnalyzer.js +20 -0
- package/lib/analyses/SwaggerExampleAnalyzer.js.map +1 -0
- package/lib/analyses/TypedHttpRouteAnalyzer.js +30 -21
- package/lib/analyses/TypedHttpRouteAnalyzer.js.map +1 -1
- package/lib/analyses/TypedWebSocketRouteAnalyzer.js +1 -1
- package/lib/analyses/TypedWebSocketRouteAnalyzer.js.map +1 -1
- package/lib/executable/internal/NestiaSdkWatcher.js +3 -3
- package/lib/executable/internal/NestiaSdkWatcher.js.map +1 -1
- package/lib/executable/sdk.js +16 -1
- package/lib/executable/sdk.js.map +1 -1
- package/lib/factories/ExpressionFactory.d.ts +3 -0
- package/lib/factories/ExpressionFactory.js +6 -2
- package/lib/factories/ExpressionFactory.js.map +1 -1
- package/lib/factories/LiteralFactory.js +3 -1
- package/lib/factories/LiteralFactory.js.map +1 -1
- package/lib/generates/SdkGenerator.js +3 -3
- package/lib/generates/SdkGenerator.js.map +1 -1
- package/lib/generates/SwaggerGenerator.js +135 -27
- package/lib/generates/SwaggerGenerator.js.map +1 -1
- package/lib/generates/internal/FilePrinter.d.ts +14 -0
- package/lib/generates/internal/FilePrinter.js +31 -0
- package/lib/generates/internal/FilePrinter.js.map +1 -1
- package/lib/generates/internal/ImportDictionary.d.ts +2 -0
- package/lib/generates/internal/ImportDictionary.js +13 -0
- package/lib/generates/internal/ImportDictionary.js.map +1 -1
- package/lib/generates/internal/SdkAliasCollection.d.ts +2 -1
- package/lib/generates/internal/SdkAliasCollection.js +8 -4
- package/lib/generates/internal/SdkAliasCollection.js.map +1 -1
- package/lib/generates/internal/SdkDistributionComposer.js +72 -14
- package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
- package/lib/generates/internal/SdkFileProgrammer.js +83 -12
- package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpFunctionProgrammer.js +21 -23
- package/lib/generates/internal/SdkHttpFunctionProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpNamespaceProgrammer.js +27 -34
- package/lib/generates/internal/SdkHttpNamespaceProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpParameterProgrammer.d.ts +40 -1
- package/lib/generates/internal/SdkHttpParameterProgrammer.js +105 -6
- package/lib/generates/internal/SdkHttpParameterProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpRouteProgrammer.js +16 -32
- package/lib/generates/internal/SdkHttpRouteProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpSimulationProgrammer.js +18 -14
- package/lib/generates/internal/SdkHttpSimulationProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkMcpRouteProgrammer.js +39 -11
- package/lib/generates/internal/SdkMcpRouteProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkPathTemplate.d.ts +21 -0
- package/lib/generates/internal/SdkPathTemplate.js +50 -0
- package/lib/generates/internal/SdkPathTemplate.js.map +1 -0
- package/lib/generates/internal/SdkTypeProgrammer.js +15 -15
- package/lib/generates/internal/SdkTypeProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkTypeTagProgrammer.d.ts +20 -1
- package/lib/generates/internal/SdkTypeTagProgrammer.js +386 -67
- package/lib/generates/internal/SdkTypeTagProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.js +27 -36
- package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketParameterProgrammer.d.ts +35 -1
- package/lib/generates/internal/SdkWebSocketParameterProgrammer.js +72 -5
- package/lib/generates/internal/SdkWebSocketParameterProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketRouteProgrammer.js +57 -33
- package/lib/generates/internal/SdkWebSocketRouteProgrammer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationComposer.js +19 -8
- package/lib/generates/internal/SwaggerOperationComposer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationParameterComposer.d.ts +1 -1
- package/lib/generates/internal/SwaggerOperationParameterComposer.js +201 -19
- package/lib/generates/internal/SwaggerOperationParameterComposer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationResponseComposer.d.ts +2 -0
- package/lib/generates/internal/SwaggerOperationResponseComposer.js +39 -7
- package/lib/generates/internal/SwaggerOperationResponseComposer.js.map +1 -1
- package/lib/internal/legacy.d.ts +49 -18
- package/lib/internal/legacy.js +84 -25
- package/lib/internal/legacy.js.map +1 -1
- package/lib/structures/IOperationMetadata.d.ts +22 -0
- package/lib/structures/IReflectHttpOperationException.d.ts +3 -2
- package/lib/structures/IReflectHttpOperationParameter.d.ts +8 -3
- package/lib/structures/IReflectHttpOperationSuccess.d.ts +4 -3
- package/lib/structures/ITypedHttpRoute.d.ts +8 -0
- package/lib/structures/ITypedHttpRouteException.d.ts +3 -1
- package/lib/structures/ITypedHttpRouteParameter.d.ts +3 -1
- package/lib/structures/ITypedHttpRouteSuccess.d.ts +3 -1
- package/lib/structures/ITypedWebSocketRoute.d.ts +8 -0
- package/lib/utils/SourceFinder.d.ts +10 -0
- package/lib/utils/SourceFinder.js +27 -4
- package/lib/utils/SourceFinder.js.map +1 -1
- package/lib/utils/VersioningStrategy.d.ts +13 -1
- package/lib/utils/VersioningStrategy.js +24 -12
- package/lib/utils/VersioningStrategy.js.map +1 -1
- package/native/go.mod +1 -1
- package/native/go.sum +2 -2
- package/native/sdk/sdk_http_rules.go +210 -0
- package/native/sdk/sdk_metadata_json.go +73 -3
- package/native/sdk/sdk_transform.go +801 -49
- package/package.json +8 -8
- package/src/INestiaConfig.ts +19 -10
- package/src/NestiaSdkApplication.ts +63 -34
- package/src/NestiaSwaggerComposer.ts +11 -8
- package/src/analyses/ConfigAnalyzer.ts +3 -3
- package/src/analyses/ParameterNameAnalyzer.ts +51 -0
- package/src/analyses/PathAnalyzer.ts +106 -14
- package/src/analyses/ReflectControllerAnalyzer.ts +2 -1
- package/src/analyses/ReflectHttpOperationAnalyzer.ts +4 -4
- package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +122 -39
- package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +6 -7
- package/src/analyses/ReflectMcpOperationAnalyzer.ts +6 -2
- package/src/analyses/ReflectWebSocketOperationAnalyzer.ts +7 -3
- package/src/analyses/SecurityAnalyzer.ts +30 -20
- package/src/analyses/SwaggerExampleAnalyzer.ts +23 -0
- package/src/analyses/TypedHttpRouteAnalyzer.ts +29 -20
- package/src/analyses/TypedWebSocketRouteAnalyzer.ts +1 -0
- package/src/executable/internal/NestiaSdkWatcher.ts +2 -2
- package/src/executable/sdk.ts +24 -1
- package/src/factories/ExpressionFactory.ts +8 -2
- package/src/factories/LiteralFactory.ts +7 -2
- package/src/generates/SdkGenerator.ts +3 -3
- package/src/generates/SwaggerGenerator.ts +173 -32
- package/src/generates/internal/FilePrinter.ts +28 -0
- package/src/generates/internal/ImportDictionary.ts +11 -0
- package/src/generates/internal/SdkAliasCollection.ts +11 -4
- package/src/generates/internal/SdkDistributionComposer.ts +89 -15
- package/src/generates/internal/SdkFileProgrammer.ts +108 -16
- package/src/generates/internal/SdkHttpFunctionProgrammer.ts +28 -30
- package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +34 -64
- package/src/generates/internal/SdkHttpParameterProgrammer.ts +193 -13
- package/src/generates/internal/SdkHttpRouteProgrammer.ts +17 -31
- package/src/generates/internal/SdkHttpSimulationProgrammer.ts +51 -14
- package/src/generates/internal/SdkMcpRouteProgrammer.ts +112 -15
- package/src/generates/internal/SdkPathTemplate.ts +73 -0
- package/src/generates/internal/SdkTypeProgrammer.ts +19 -22
- package/src/generates/internal/SdkTypeTagProgrammer.ts +506 -78
- package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +43 -68
- package/src/generates/internal/SdkWebSocketParameterProgrammer.ts +126 -5
- package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +71 -48
- package/src/generates/internal/SwaggerOperationComposer.ts +20 -6
- package/src/generates/internal/SwaggerOperationParameterComposer.ts +279 -30
- package/src/generates/internal/SwaggerOperationResponseComposer.ts +55 -11
- package/src/internal/legacy.ts +114 -26
- package/src/structures/IOperationMetadata.ts +28 -0
- package/src/structures/IReflectHttpOperationException.ts +7 -2
- package/src/structures/IReflectHttpOperationParameter.ts +9 -2
- package/src/structures/IReflectHttpOperationSuccess.ts +8 -3
- package/src/structures/ITypedHttpRoute.ts +8 -0
- package/src/structures/ITypedHttpRouteException.ts +4 -1
- package/src/structures/ITypedHttpRouteParameter.ts +3 -1
- package/src/structures/ITypedHttpRouteSuccess.ts +4 -1
- package/src/structures/ITypedWebSocketRoute.ts +8 -0
- package/src/utils/SourceFinder.ts +25 -4
- package/src/utils/VersioningStrategy.ts +28 -13
- package/lib/validators/HttpHeadersValidator.d.ts +0 -11
- package/lib/validators/HttpHeadersValidator.js +0 -15
- package/lib/validators/HttpHeadersValidator.js.map +0 -1
- package/lib/validators/HttpQueryValidator.d.ts +0 -10
- package/lib/validators/HttpQueryValidator.js +0 -14
- package/lib/validators/HttpQueryValidator.js.map +0 -1
- package/src/validators/HttpHeadersValidator.ts +0 -12
- package/src/validators/HttpQueryValidator.ts +0 -11
|
@@ -2,40 +2,47 @@ import { type TypeNode, factory } from "@ttsc/factory";
|
|
|
2
2
|
import { IMetadataTypeTag } from "@typia/interface";
|
|
3
3
|
|
|
4
4
|
import { LiteralFactory } from "../../factories/LiteralFactory";
|
|
5
|
+
import { decodeTagValue } from "../../internal/legacy";
|
|
5
6
|
import { ImportDictionary } from "./ImportDictionary";
|
|
6
7
|
|
|
7
8
|
export namespace SdkTypeTagProgrammer {
|
|
9
|
+
export type Target =
|
|
10
|
+
| "object"
|
|
11
|
+
| "array"
|
|
12
|
+
| "boolean"
|
|
13
|
+
| "number"
|
|
14
|
+
| "bigint"
|
|
15
|
+
| "string";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Writes a type tag of a cloned DTO.
|
|
19
|
+
*
|
|
20
|
+
* A tag prints as the predefined typia tag it matches, `tags.Minimum<3>`,
|
|
21
|
+
* only when that tag accepts the argument and, given it, expands to exactly
|
|
22
|
+
* the tag the metadata carries; then the two are the same type. Anything else
|
|
23
|
+
* prints in the generic `tags.TagBase<{ ... }>` form, which is always the
|
|
24
|
+
* same type.
|
|
25
|
+
*
|
|
26
|
+
* The tag's name cannot decide it. The SDK names a tag after its object type
|
|
27
|
+
* with the brackets and quotes a component name drops, `Minimum3` or
|
|
28
|
+
* `Formatuuid`, so a name no longer tells `tags.Minimum<3>` from a user's own
|
|
29
|
+
* tag named alike (#1662). The expansion can: it is typia's declaration of
|
|
30
|
+
* each predefined tag, and should typia change one, the tag only falls back
|
|
31
|
+
* to the `TagBase` form.
|
|
32
|
+
*/
|
|
8
33
|
export const write = (
|
|
9
34
|
importer: ImportDictionary,
|
|
10
|
-
from:
|
|
35
|
+
from: Target,
|
|
11
36
|
tag: IMetadataTypeTag,
|
|
12
37
|
): TypeNode => {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
importer.external({
|
|
19
|
-
declaration: true,
|
|
20
|
-
file: `typia`,
|
|
21
|
-
type: "element",
|
|
22
|
-
name: "tags",
|
|
23
|
-
}),
|
|
24
|
-
),
|
|
25
|
-
factory.createIdentifier(name),
|
|
26
|
-
),
|
|
27
|
-
[factory.createLiteralTypeNode(LiteralFactory.write(tag.value) as any)],
|
|
28
|
-
);
|
|
38
|
+
const predefined: IPredefined | null =
|
|
39
|
+
comment(from, tag) ?? recognize(from, tag);
|
|
40
|
+
if (predefined !== null)
|
|
41
|
+
return writePredefined(importer, predefined.name, predefined.argument);
|
|
42
|
+
const value: unknown = decodeTagValue(tag);
|
|
29
43
|
return factory.createTypeReferenceNode(
|
|
30
44
|
factory.createQualifiedName(
|
|
31
|
-
factory.createIdentifier(
|
|
32
|
-
importer.external({
|
|
33
|
-
declaration: true,
|
|
34
|
-
file: `typia`,
|
|
35
|
-
type: "element",
|
|
36
|
-
name: "tags",
|
|
37
|
-
}),
|
|
38
|
-
),
|
|
45
|
+
factory.createIdentifier(tagsOf(importer)),
|
|
39
46
|
factory.createIdentifier("TagBase"),
|
|
40
47
|
),
|
|
41
48
|
[
|
|
@@ -43,7 +50,7 @@ export namespace SdkTypeTagProgrammer {
|
|
|
43
50
|
LiteralFactory.write({
|
|
44
51
|
target: from,
|
|
45
52
|
kind: tag.kind,
|
|
46
|
-
value:
|
|
53
|
+
value: Number.isNaN(value) ? null : value,
|
|
47
54
|
validate: tag.validate,
|
|
48
55
|
exclusive: tag.exclusive,
|
|
49
56
|
schema: tag.schema,
|
|
@@ -52,59 +59,480 @@ export namespace SdkTypeTagProgrammer {
|
|
|
52
59
|
],
|
|
53
60
|
);
|
|
54
61
|
};
|
|
62
|
+
|
|
63
|
+
/** Writes the predefined typia tag `tags.<name><argument>`. */
|
|
64
|
+
export const writePredefined = (
|
|
65
|
+
importer: ImportDictionary,
|
|
66
|
+
name: string,
|
|
67
|
+
argument: unknown,
|
|
68
|
+
): TypeNode =>
|
|
69
|
+
factory.createTypeReferenceNode(
|
|
70
|
+
factory.createQualifiedName(
|
|
71
|
+
factory.createIdentifier(tagsOf(importer)),
|
|
72
|
+
factory.createIdentifier(name),
|
|
73
|
+
),
|
|
74
|
+
[factory.createLiteralTypeNode(LiteralFactory.write(argument) as any)],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const tagsOf = (importer: ImportDictionary): string =>
|
|
78
|
+
importer.external({
|
|
79
|
+
declaration: true,
|
|
80
|
+
file: `typia`,
|
|
81
|
+
type: "element",
|
|
82
|
+
name: "tags",
|
|
83
|
+
});
|
|
55
84
|
}
|
|
56
85
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
interface IPredefined {
|
|
87
|
+
name: string;
|
|
88
|
+
argument: unknown;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The predefined tag a JSDoc comment tag stands for.
|
|
93
|
+
*
|
|
94
|
+
* Typia names a comment tag, `@format uri`, after that tag with its brackets,
|
|
95
|
+
* `Format<"uri">`, while a type tag's name has none, as the SDK's component
|
|
96
|
+
* names drop them. The source has no type tag to equal, and a comment tag may
|
|
97
|
+
* validate its own way, such as a format's inlined expression, so it prints as
|
|
98
|
+
* the tag the comment names whenever the argument suits the tagged type.
|
|
99
|
+
*/
|
|
100
|
+
const comment = (
|
|
101
|
+
from: SdkTypeTagProgrammer.Target,
|
|
102
|
+
tag: IMetadataTypeTag,
|
|
103
|
+
): IPredefined | null => {
|
|
104
|
+
if (tag.name.includes("<") === false) return null;
|
|
105
|
+
const definition: IDefinition | undefined = DEFINITIONS[tag.kind];
|
|
106
|
+
if (definition === undefined || tag.name.split("<")[0] !== definition.name)
|
|
107
|
+
return null;
|
|
108
|
+
const argument: unknown = argumentOf(definition, tag);
|
|
109
|
+
return argument !== undefined && definition.expand(from, argument) !== null
|
|
110
|
+
? { name: definition.name, argument }
|
|
111
|
+
: null;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The typia tag the metadata's type tag is, with the argument that makes it, or
|
|
116
|
+
* `null` when no predefined tag expands to it exactly.
|
|
117
|
+
*/
|
|
118
|
+
const recognize = (
|
|
119
|
+
from: SdkTypeTagProgrammer.Target,
|
|
120
|
+
tag: IMetadataTypeTag,
|
|
121
|
+
): IPredefined | null => {
|
|
122
|
+
const definition: IDefinition | undefined = DEFINITIONS[tag.kind];
|
|
123
|
+
if (definition === undefined) return null;
|
|
124
|
+
const argument: unknown = argumentOf(definition, tag);
|
|
125
|
+
if (argument === undefined) return null;
|
|
126
|
+
const expected: IExpansion | null = definition.expand(from, argument);
|
|
127
|
+
if (expected === null) return null;
|
|
128
|
+
// the metadata holds the schema as JSON holds it
|
|
129
|
+
return equals(expected.validate, tag.validate) &&
|
|
130
|
+
equals(expected.exclusive, tag.exclusive) &&
|
|
131
|
+
equals(json(expected.schema), tag.schema)
|
|
132
|
+
? { name: definition.name, argument }
|
|
133
|
+
: null;
|
|
88
134
|
};
|
|
89
135
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
/**
|
|
137
|
+
* The type argument a tag was written with: its value, or, for a tag whose
|
|
138
|
+
* value is no literal, the schema member holding it. NaN has no literal type to
|
|
139
|
+
* write, so it gives none.
|
|
140
|
+
*/
|
|
141
|
+
const argumentOf = (
|
|
142
|
+
definition: IDefinition,
|
|
143
|
+
tag: IMetadataTypeTag,
|
|
144
|
+
): unknown => {
|
|
145
|
+
const value: unknown = decodeTagValue(tag);
|
|
146
|
+
const argument: unknown =
|
|
147
|
+
value === null || value === undefined
|
|
148
|
+
? definition.fallback?.(tag.schema as Record<string, unknown>)
|
|
149
|
+
: value;
|
|
150
|
+
return Number.isNaN(argument) ? undefined : argument;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* What a predefined tag, given its argument, bakes into the metadata. A tag
|
|
155
|
+
* whose type parameter constraint rejects the argument expands to nothing.
|
|
156
|
+
*/
|
|
157
|
+
interface IExpansion {
|
|
158
|
+
validate: string | undefined;
|
|
159
|
+
exclusive: boolean | string[];
|
|
160
|
+
schema: object;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
interface IDefinition {
|
|
164
|
+
name: string;
|
|
165
|
+
expand: (
|
|
166
|
+
from: SdkTypeTagProgrammer.Target,
|
|
167
|
+
argument: unknown,
|
|
168
|
+
) => IExpansion | null;
|
|
169
|
+
fallback?: (schema: Record<string, unknown> | undefined) => unknown;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** `${Value}` of a numeric type argument, as a TypeScript template prints it. */
|
|
173
|
+
const numeral = (value: number | bigint): string =>
|
|
174
|
+
typeof value === "bigint" ? `BigInt(${value})` : String(value);
|
|
175
|
+
|
|
176
|
+
const isNumeric = (value: unknown): value is number | bigint =>
|
|
177
|
+
typeof value === "number" || typeof value === "bigint";
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* A value as JSON holds it: a bigint as its number, as typia's `Numeric<Value>`
|
|
181
|
+
* does, and a non-finite number as null, as `JSON.stringify` writes it.
|
|
182
|
+
*/
|
|
183
|
+
const json = (value: unknown): unknown =>
|
|
184
|
+
typeof value === "bigint"
|
|
185
|
+
? Number(value)
|
|
186
|
+
: typeof value === "number" && Number.isFinite(value) === false
|
|
187
|
+
? null
|
|
188
|
+
: Array.isArray(value)
|
|
189
|
+
? value.map(json)
|
|
190
|
+
: typeof value === "object" && value !== null
|
|
191
|
+
? Object.fromEntries(
|
|
192
|
+
Object.entries(value).map(([key, v]) => [key, json(v)]),
|
|
193
|
+
)
|
|
194
|
+
: value;
|
|
195
|
+
|
|
196
|
+
const bound =
|
|
197
|
+
(
|
|
198
|
+
kind: string,
|
|
199
|
+
exclusive: string[],
|
|
200
|
+
validate: (value: string) => string,
|
|
201
|
+
): IDefinition["expand"] =>
|
|
202
|
+
(from, argument) =>
|
|
203
|
+
isNumeric(argument) &&
|
|
204
|
+
from === (typeof argument === "bigint" ? "bigint" : "number")
|
|
205
|
+
? {
|
|
206
|
+
validate: validate(numeral(argument)),
|
|
207
|
+
exclusive,
|
|
208
|
+
schema: { [kind]: json(argument) },
|
|
209
|
+
}
|
|
210
|
+
: null;
|
|
211
|
+
|
|
212
|
+
const TYPE_VALIDATE: Record<string, string | Record<string, string>> = {
|
|
213
|
+
int8: `$importInternal("isTypeInt8")($input)`,
|
|
214
|
+
uint8: `$importInternal("isTypeUint8")($input)`,
|
|
215
|
+
int16: `$importInternal("isTypeInt16")($input)`,
|
|
216
|
+
uint16: `$importInternal("isTypeUint16")($input)`,
|
|
217
|
+
int32: `$importInternal("isTypeInt32")($input)`,
|
|
218
|
+
uint32: `$importInternal("isTypeUint32")($input)`,
|
|
219
|
+
int64: {
|
|
220
|
+
number: `$importInternal("isTypeInt64")($input)`,
|
|
221
|
+
bigint: `$importInternal("isTypeInt64Bigint")($input)`,
|
|
222
|
+
},
|
|
223
|
+
uint64: {
|
|
224
|
+
number: `$importInternal("isTypeUint64")($input)`,
|
|
225
|
+
bigint: `$importInternal("isTypeUint64Bigint")($input)`,
|
|
226
|
+
},
|
|
227
|
+
float: `$importInternal("isTypeFloat")($input)`,
|
|
228
|
+
double: `true`,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/** `Format.Value` of typia's `Format` tag. */
|
|
232
|
+
const FORMATS: Set<string> = new Set([
|
|
233
|
+
"byte",
|
|
234
|
+
"password",
|
|
235
|
+
"regex",
|
|
236
|
+
"uuid",
|
|
237
|
+
"email",
|
|
238
|
+
"hostname",
|
|
239
|
+
"idn-email",
|
|
240
|
+
"idn-hostname",
|
|
241
|
+
"iri",
|
|
242
|
+
"iri-reference",
|
|
243
|
+
"ipv4",
|
|
244
|
+
"ipv6",
|
|
245
|
+
"uri",
|
|
246
|
+
"uri-reference",
|
|
247
|
+
"uri-template",
|
|
248
|
+
"url",
|
|
249
|
+
"date-time",
|
|
250
|
+
"date",
|
|
251
|
+
"time",
|
|
252
|
+
"duration",
|
|
253
|
+
"json-pointer",
|
|
254
|
+
"relative-json-pointer",
|
|
255
|
+
]);
|
|
256
|
+
|
|
257
|
+
/** `DefaultAtomic` of typia's `Default` tag. */
|
|
258
|
+
const isDefaultAtomic = (value: unknown): boolean =>
|
|
259
|
+
typeof value === "boolean" ||
|
|
260
|
+
typeof value === "bigint" ||
|
|
261
|
+
typeof value === "number" ||
|
|
262
|
+
typeof value === "string";
|
|
263
|
+
|
|
264
|
+
/** `PascalizeString` of typia's `Format` tag: `date-time` as `DateTime`. */
|
|
265
|
+
const pascalize = (value: string): string =>
|
|
266
|
+
value.includes("-")
|
|
267
|
+
? value
|
|
268
|
+
.split("-")
|
|
269
|
+
.filter((word) => word.length !== 0)
|
|
270
|
+
.map(
|
|
271
|
+
(word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),
|
|
272
|
+
)
|
|
273
|
+
.join("")
|
|
274
|
+
: value.charAt(0).toUpperCase() + value.slice(1);
|
|
275
|
+
|
|
276
|
+
/** `Serialize` of typia's `Pattern` tag, escaping as a string literal. */
|
|
277
|
+
const serialize = (value: string): string =>
|
|
278
|
+
value.replace(
|
|
279
|
+
/["\\\b\f\n\r\t]/g,
|
|
280
|
+
(char) =>
|
|
281
|
+
({
|
|
282
|
+
'"': '\\"',
|
|
283
|
+
"\\": "\\\\",
|
|
284
|
+
"\b": "\\b",
|
|
285
|
+
"\f": "\\f",
|
|
286
|
+
"\n": "\\n",
|
|
287
|
+
"\r": "\\r",
|
|
288
|
+
"\t": "\\t",
|
|
289
|
+
})[char]!,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Every predefined typia tag, keyed by its kind, as `@typia/interface` declares
|
|
294
|
+
* it.
|
|
295
|
+
*/
|
|
296
|
+
const DEFINITIONS: Record<string, IDefinition> = {
|
|
297
|
+
minimum: {
|
|
298
|
+
name: "Minimum",
|
|
299
|
+
expand: bound(
|
|
300
|
+
"minimum",
|
|
301
|
+
["minimum", "exclusiveMinimum"],
|
|
302
|
+
(v) => `${v} <= $input`,
|
|
303
|
+
),
|
|
304
|
+
},
|
|
305
|
+
maximum: {
|
|
306
|
+
name: "Maximum",
|
|
307
|
+
expand: bound(
|
|
308
|
+
"maximum",
|
|
309
|
+
["maximum", "exclusiveMaximum"],
|
|
310
|
+
(v) => `$input <= ${v}`,
|
|
311
|
+
),
|
|
312
|
+
},
|
|
313
|
+
exclusiveMinimum: {
|
|
314
|
+
name: "ExclusiveMinimum",
|
|
315
|
+
expand: bound(
|
|
316
|
+
"exclusiveMinimum",
|
|
317
|
+
["exclusiveMinimum", "minimum"],
|
|
318
|
+
(v) => `${v} < $input`,
|
|
319
|
+
),
|
|
320
|
+
},
|
|
321
|
+
exclusiveMaximum: {
|
|
322
|
+
name: "ExclusiveMaximum",
|
|
323
|
+
expand: bound(
|
|
324
|
+
"exclusiveMaximum",
|
|
325
|
+
["exclusiveMaximum", "maximum"],
|
|
326
|
+
(v) => `$input < ${v}`,
|
|
327
|
+
),
|
|
328
|
+
},
|
|
329
|
+
multipleOf: {
|
|
330
|
+
name: "MultipleOf",
|
|
331
|
+
expand: (from, argument) =>
|
|
332
|
+
isNumeric(argument) &&
|
|
333
|
+
from === (typeof argument === "bigint" ? "bigint" : "number")
|
|
334
|
+
? {
|
|
335
|
+
validate:
|
|
336
|
+
typeof argument === "bigint"
|
|
337
|
+
? `$input % ${numeral(argument)} === ${numeral(BigInt(0))}`
|
|
338
|
+
: `$importInternal("_isMultipleOf")($input, ${argument})`,
|
|
339
|
+
exclusive: true,
|
|
340
|
+
schema: { multipleOf: json(argument) },
|
|
341
|
+
}
|
|
342
|
+
: null,
|
|
343
|
+
},
|
|
344
|
+
type: {
|
|
345
|
+
name: "Type",
|
|
346
|
+
expand: (from, argument) => {
|
|
347
|
+
if (typeof argument !== "string") return null;
|
|
348
|
+
const validate = TYPE_VALIDATE[argument];
|
|
349
|
+
if (validate === undefined) return null;
|
|
350
|
+
const wide: boolean = argument === "int64" || argument === "uint64";
|
|
351
|
+
if (from !== "number" && (wide === false || from !== "bigint"))
|
|
352
|
+
return null;
|
|
353
|
+
return {
|
|
354
|
+
validate: typeof validate === "string" ? validate : validate[from],
|
|
355
|
+
exclusive: true,
|
|
356
|
+
schema: argument.startsWith("uint")
|
|
357
|
+
? { type: "integer", minimum: 0 }
|
|
358
|
+
: { type: argument.startsWith("int") ? "integer" : "number" },
|
|
359
|
+
};
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
format: {
|
|
363
|
+
name: "Format",
|
|
364
|
+
expand: (from, argument) =>
|
|
365
|
+
from === "string" && typeof argument === "string" && FORMATS.has(argument)
|
|
366
|
+
? {
|
|
367
|
+
validate: `$importInternal("isFormat${pascalize(argument)}")($input)`,
|
|
368
|
+
exclusive: ["format", "pattern"],
|
|
369
|
+
schema: { format: argument },
|
|
370
|
+
}
|
|
371
|
+
: null,
|
|
372
|
+
},
|
|
373
|
+
pattern: {
|
|
374
|
+
name: "Pattern",
|
|
375
|
+
expand: (from, argument) =>
|
|
376
|
+
from === "string" && typeof argument === "string"
|
|
377
|
+
? {
|
|
378
|
+
validate: `RegExp("${serialize(argument)}").test($input)`,
|
|
379
|
+
exclusive: ["format", "pattern"],
|
|
380
|
+
schema: { pattern: argument },
|
|
381
|
+
}
|
|
382
|
+
: null,
|
|
383
|
+
},
|
|
384
|
+
minLength: {
|
|
385
|
+
name: "MinLength",
|
|
386
|
+
expand: (from, argument) =>
|
|
387
|
+
from === "string" && typeof argument === "number"
|
|
388
|
+
? {
|
|
389
|
+
validate: `$importInternal("_stringLengthGte")($input, ${argument})`,
|
|
390
|
+
exclusive: true,
|
|
391
|
+
schema: { minLength: argument },
|
|
392
|
+
}
|
|
393
|
+
: null,
|
|
394
|
+
},
|
|
395
|
+
maxLength: {
|
|
396
|
+
name: "MaxLength",
|
|
397
|
+
expand: (from, argument) =>
|
|
398
|
+
from === "string" && typeof argument === "number"
|
|
399
|
+
? {
|
|
400
|
+
validate: `$importInternal("_stringLengthLte")($input, ${argument})`,
|
|
401
|
+
exclusive: true,
|
|
402
|
+
schema: { maxLength: argument },
|
|
403
|
+
}
|
|
404
|
+
: null,
|
|
405
|
+
},
|
|
406
|
+
minItems: {
|
|
407
|
+
name: "MinItems",
|
|
408
|
+
expand: (from, argument) =>
|
|
409
|
+
from === "array" && typeof argument === "number"
|
|
410
|
+
? {
|
|
411
|
+
validate: `${argument} <= $input.length`,
|
|
412
|
+
exclusive: true,
|
|
413
|
+
schema: { minItems: argument },
|
|
414
|
+
}
|
|
415
|
+
: null,
|
|
416
|
+
},
|
|
417
|
+
maxItems: {
|
|
418
|
+
name: "MaxItems",
|
|
419
|
+
expand: (from, argument) =>
|
|
420
|
+
from === "array" && typeof argument === "number"
|
|
421
|
+
? {
|
|
422
|
+
validate: `$input.length <= ${argument}`,
|
|
423
|
+
exclusive: true,
|
|
424
|
+
schema: { maxItems: argument },
|
|
425
|
+
}
|
|
426
|
+
: null,
|
|
427
|
+
},
|
|
428
|
+
uniqueItems: {
|
|
429
|
+
name: "UniqueItems",
|
|
430
|
+
expand: (from, argument) =>
|
|
431
|
+
from === "array" && typeof argument === "boolean"
|
|
432
|
+
? {
|
|
433
|
+
validate: argument
|
|
434
|
+
? `$importInternal("isUniqueItems")($input)`
|
|
435
|
+
: undefined,
|
|
436
|
+
exclusive: true,
|
|
437
|
+
schema: { uniqueItems: true },
|
|
438
|
+
}
|
|
439
|
+
: null,
|
|
440
|
+
},
|
|
441
|
+
contentMediaType: {
|
|
442
|
+
name: "ContentMediaType",
|
|
443
|
+
expand: (from, argument) =>
|
|
444
|
+
from === "string" && typeof argument === "string"
|
|
445
|
+
? {
|
|
446
|
+
validate: undefined,
|
|
447
|
+
exclusive: false,
|
|
448
|
+
schema: { contentMediaType: argument },
|
|
449
|
+
}
|
|
450
|
+
: null,
|
|
451
|
+
fallback: (schema) => schema?.contentMediaType,
|
|
452
|
+
},
|
|
453
|
+
default: {
|
|
454
|
+
name: "Default",
|
|
455
|
+
expand: (from, argument) =>
|
|
456
|
+
from ===
|
|
457
|
+
(Array.isArray(argument)
|
|
458
|
+
? argument.every(isDefaultAtomic)
|
|
459
|
+
? "array"
|
|
460
|
+
: null
|
|
461
|
+
: typeof argument === "boolean"
|
|
462
|
+
? "boolean"
|
|
463
|
+
: typeof argument === "bigint"
|
|
464
|
+
? "bigint"
|
|
465
|
+
: typeof argument === "number"
|
|
466
|
+
? "number"
|
|
467
|
+
: typeof argument === "string"
|
|
468
|
+
? "string"
|
|
469
|
+
: null)
|
|
470
|
+
? {
|
|
471
|
+
validate: undefined,
|
|
472
|
+
exclusive: true,
|
|
473
|
+
schema: { default: json(argument) },
|
|
474
|
+
}
|
|
475
|
+
: null,
|
|
476
|
+
fallback: (schema) => schema?.default,
|
|
477
|
+
},
|
|
478
|
+
example: {
|
|
479
|
+
name: "Example",
|
|
480
|
+
expand: (_from, argument) => ({
|
|
481
|
+
validate: undefined,
|
|
482
|
+
exclusive: true,
|
|
483
|
+
schema: {
|
|
484
|
+
example: typeof argument === "bigint" ? Number(argument) : argument,
|
|
485
|
+
},
|
|
486
|
+
}),
|
|
487
|
+
fallback: (schema) => schema?.example,
|
|
488
|
+
},
|
|
489
|
+
examples: {
|
|
490
|
+
name: "Examples",
|
|
491
|
+
expand: (_from, argument) =>
|
|
492
|
+
typeof argument === "object" &&
|
|
493
|
+
argument !== null &&
|
|
494
|
+
Array.isArray(argument) === false &&
|
|
495
|
+
Object.values(argument).every((value) => value !== undefined)
|
|
496
|
+
? {
|
|
497
|
+
validate: undefined,
|
|
498
|
+
exclusive: true,
|
|
499
|
+
schema: { examples: argument },
|
|
500
|
+
}
|
|
501
|
+
: null,
|
|
502
|
+
fallback: (schema) => schema?.examples,
|
|
503
|
+
},
|
|
504
|
+
sequence: {
|
|
505
|
+
name: "Sequence",
|
|
506
|
+
expand: (_from, argument) =>
|
|
507
|
+
typeof argument === "number"
|
|
508
|
+
? {
|
|
509
|
+
validate: undefined,
|
|
510
|
+
exclusive: false,
|
|
511
|
+
schema: { "x-protobuf-sequence": argument },
|
|
512
|
+
}
|
|
513
|
+
: null,
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
/** Structural equality of metadata values, which hold bigints. */
|
|
518
|
+
const equals = (x: unknown, y: unknown): boolean => {
|
|
519
|
+
if (x === y) return true;
|
|
520
|
+
if (typeof x !== "object" || typeof y !== "object") return false;
|
|
521
|
+
if (x === null || y === null) return false;
|
|
522
|
+
if (Array.isArray(x) !== Array.isArray(y)) return false;
|
|
523
|
+
const xKeys: string[] = Object.keys(x).filter(
|
|
524
|
+
(key) => (x as Record<string, unknown>)[key] !== undefined,
|
|
525
|
+
);
|
|
526
|
+
const yKeys: string[] = Object.keys(y).filter(
|
|
527
|
+
(key) => (y as Record<string, unknown>)[key] !== undefined,
|
|
528
|
+
);
|
|
529
|
+
return (
|
|
530
|
+
xKeys.length === yKeys.length &&
|
|
531
|
+
xKeys.every((key) =>
|
|
532
|
+
equals(
|
|
533
|
+
(x as Record<string, unknown>)[key],
|
|
534
|
+
(y as Record<string, unknown>)[key],
|
|
535
|
+
),
|
|
536
|
+
)
|
|
537
|
+
);
|
|
538
|
+
};
|