@nestia/migrate 10.0.1 → 11.0.0-dev.20260305

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 (162) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -93
  3. package/package.json +32 -46
  4. package/src/NestiaMigrateApplication.ts +161 -159
  5. package/src/analyzers/NestiaMigrateControllerAnalyzer.ts +51 -51
  6. package/src/archivers/NestiaMigrateFileArchiver.ts +28 -28
  7. package/src/bundles/NEST_TEMPLATE.ts +48 -48
  8. package/src/bundles/SDK_TEMPLATE.ts +21 -21
  9. package/src/executable/NestiaMigrateCommander.ts +98 -98
  10. package/src/executable/NestiaMigrateInquirer.ts +106 -106
  11. package/src/executable/bundle.js +125 -125
  12. package/src/executable/migrate.ts +7 -7
  13. package/src/factories/TypeLiteralFactory.ts +57 -57
  14. package/src/index.ts +4 -4
  15. package/src/module.ts +6 -2
  16. package/src/programmers/NestiaMigrateApiFileProgrammer.ts +55 -55
  17. package/src/programmers/NestiaMigrateApiFunctionProgrammer.ts +347 -344
  18. package/src/programmers/NestiaMigrateApiNamespaceProgrammer.ts +517 -514
  19. package/src/programmers/NestiaMigrateApiProgrammer.ts +107 -107
  20. package/src/programmers/NestiaMigrateApiSimulationProgrammer.ts +308 -309
  21. package/src/programmers/NestiaMigrateApiStartProgrammer.ts +197 -198
  22. package/src/programmers/NestiaMigrateDtoProgrammer.ts +98 -98
  23. package/src/programmers/NestiaMigrateE2eFileProgrammer.ts +153 -153
  24. package/src/programmers/NestiaMigrateE2eProgrammer.ts +48 -48
  25. package/src/programmers/NestiaMigrateImportProgrammer.ts +118 -118
  26. package/src/programmers/NestiaMigrateNestControllerProgrammer.ts +69 -69
  27. package/src/programmers/NestiaMigrateNestMethodProgrammer.ts +409 -406
  28. package/src/programmers/NestiaMigrateNestModuleProgrammer.ts +65 -65
  29. package/src/programmers/NestiaMigrateNestProgrammer.ts +88 -88
  30. package/src/programmers/NestiaMigrateSchemaProgrammer.ts +465 -522
  31. package/src/structures/INestiaMigrateConfig.ts +19 -19
  32. package/src/structures/INestiaMigrateContext.ts +9 -9
  33. package/src/structures/INestiaMigrateController.ts +8 -8
  34. package/src/structures/INestiaMigrateDto.ts +8 -8
  35. package/src/structures/INestiaMigrateFile.ts +5 -5
  36. package/src/structures/INestiaMigrateProgram.ts +11 -11
  37. package/src/structures/INestiaMigrateSchema.ts +4 -4
  38. package/src/utils/FilePrinter.ts +49 -49
  39. package/src/utils/MapUtil.ts +13 -13
  40. package/src/utils/SetupWizard.ts +12 -12
  41. package/src/utils/StringUtil.ts +114 -113
  42. package/src/utils/openapi-down-convert/RefVisitor.ts +134 -134
  43. package/src/utils/openapi-down-convert/converter.ts +536 -536
  44. package/lib/NestiaMigrateApplication.d.ts +0 -24
  45. package/lib/NestiaMigrateApplication.js +0 -18757
  46. package/lib/NestiaMigrateApplication.js.map +0 -1
  47. package/lib/analyzers/NestiaMigrateControllerAnalyzer.d.ts +0 -5
  48. package/lib/analyzers/NestiaMigrateControllerAnalyzer.js +0 -46
  49. package/lib/analyzers/NestiaMigrateControllerAnalyzer.js.map +0 -1
  50. package/lib/archivers/NestiaMigrateFileArchiver.d.ts +0 -8
  51. package/lib/archivers/NestiaMigrateFileArchiver.js +0 -36
  52. package/lib/archivers/NestiaMigrateFileArchiver.js.map +0 -1
  53. package/lib/bundles/NEST_TEMPLATE.d.ts +0 -1
  54. package/lib/bundles/NEST_TEMPLATE.js +0 -55
  55. package/lib/bundles/NEST_TEMPLATE.js.map +0 -1
  56. package/lib/bundles/SDK_TEMPLATE.d.ts +0 -1
  57. package/lib/bundles/SDK_TEMPLATE.js +0 -27
  58. package/lib/bundles/SDK_TEMPLATE.js.map +0 -1
  59. package/lib/executable/NestiaMigrateCommander.d.ts +0 -4
  60. package/lib/executable/NestiaMigrateCommander.js +0 -125
  61. package/lib/executable/NestiaMigrateCommander.js.map +0 -1
  62. package/lib/executable/NestiaMigrateInquirer.d.ts +0 -12
  63. package/lib/executable/NestiaMigrateInquirer.js +0 -94
  64. package/lib/executable/NestiaMigrateInquirer.js.map +0 -1
  65. package/lib/executable/migrate.d.ts +0 -2
  66. package/lib/executable/migrate.js +0 -9
  67. package/lib/executable/migrate.js.map +0 -1
  68. package/lib/factories/TypeLiteralFactory.d.ts +0 -4
  69. package/lib/factories/TypeLiteralFactory.js +0 -35
  70. package/lib/factories/TypeLiteralFactory.js.map +0 -1
  71. package/lib/index.d.ts +0 -3
  72. package/lib/index.js +0 -42
  73. package/lib/index.js.map +0 -1
  74. package/lib/index.mjs +0 -19399
  75. package/lib/index.mjs.map +0 -1
  76. package/lib/module.d.ts +0 -2
  77. package/lib/module.js +0 -19
  78. package/lib/module.js.map +0 -1
  79. package/lib/programmers/NestiaMigrateApiFileProgrammer.d.ts +0 -13
  80. package/lib/programmers/NestiaMigrateApiFileProgrammer.js +0 -40
  81. package/lib/programmers/NestiaMigrateApiFileProgrammer.js.map +0 -1
  82. package/lib/programmers/NestiaMigrateApiFunctionProgrammer.d.ts +0 -14
  83. package/lib/programmers/NestiaMigrateApiFunctionProgrammer.js +0 -179
  84. package/lib/programmers/NestiaMigrateApiFunctionProgrammer.js.map +0 -1
  85. package/lib/programmers/NestiaMigrateApiNamespaceProgrammer.d.ts +0 -14
  86. package/lib/programmers/NestiaMigrateApiNamespaceProgrammer.js +0 -226
  87. package/lib/programmers/NestiaMigrateApiNamespaceProgrammer.js.map +0 -1
  88. package/lib/programmers/NestiaMigrateApiProgrammer.d.ts +0 -4
  89. package/lib/programmers/NestiaMigrateApiProgrammer.js +0 -82
  90. package/lib/programmers/NestiaMigrateApiProgrammer.js.map +0 -1
  91. package/lib/programmers/NestiaMigrateApiSimulationProgrammer.d.ts +0 -14
  92. package/lib/programmers/NestiaMigrateApiSimulationProgrammer.js +0 -142
  93. package/lib/programmers/NestiaMigrateApiSimulationProgrammer.js.map +0 -1
  94. package/lib/programmers/NestiaMigrateApiStartProgrammer.d.ts +0 -4
  95. package/lib/programmers/NestiaMigrateApiStartProgrammer.js +0 -80
  96. package/lib/programmers/NestiaMigrateApiStartProgrammer.js.map +0 -1
  97. package/lib/programmers/NestiaMigrateDtoProgrammer.d.ts +0 -15
  98. package/lib/programmers/NestiaMigrateDtoProgrammer.js +0 -65
  99. package/lib/programmers/NestiaMigrateDtoProgrammer.js.map +0 -1
  100. package/lib/programmers/NestiaMigrateE2eFileProgrammer.d.ts +0 -14
  101. package/lib/programmers/NestiaMigrateE2eFileProgrammer.js +0 -81
  102. package/lib/programmers/NestiaMigrateE2eFileProgrammer.js.map +0 -1
  103. package/lib/programmers/NestiaMigrateE2eProgrammer.d.ts +0 -4
  104. package/lib/programmers/NestiaMigrateE2eProgrammer.js +0 -32
  105. package/lib/programmers/NestiaMigrateE2eProgrammer.js.map +0 -1
  106. package/lib/programmers/NestiaMigrateImportProgrammer.d.ts +0 -18
  107. package/lib/programmers/NestiaMigrateImportProgrammer.js +0 -65
  108. package/lib/programmers/NestiaMigrateImportProgrammer.js.map +0 -1
  109. package/lib/programmers/NestiaMigrateNestControllerProgrammer.d.ts +0 -12
  110. package/lib/programmers/NestiaMigrateNestControllerProgrammer.js +0 -46
  111. package/lib/programmers/NestiaMigrateNestControllerProgrammer.js.map +0 -1
  112. package/lib/programmers/NestiaMigrateNestMethodProgrammer.d.ts +0 -15
  113. package/lib/programmers/NestiaMigrateNestMethodProgrammer.js +0 -224
  114. package/lib/programmers/NestiaMigrateNestMethodProgrammer.js.map +0 -1
  115. package/lib/programmers/NestiaMigrateNestModuleProgrammer.d.ts +0 -5
  116. package/lib/programmers/NestiaMigrateNestModuleProgrammer.js +0 -29
  117. package/lib/programmers/NestiaMigrateNestModuleProgrammer.js.map +0 -1
  118. package/lib/programmers/NestiaMigrateNestProgrammer.d.ts +0 -4
  119. package/lib/programmers/NestiaMigrateNestProgrammer.js +0 -65
  120. package/lib/programmers/NestiaMigrateNestProgrammer.js.map +0 -1
  121. package/lib/programmers/NestiaMigrateSchemaProgrammer.d.ts +0 -10
  122. package/lib/programmers/NestiaMigrateSchemaProgrammer.js +0 -469
  123. package/lib/programmers/NestiaMigrateSchemaProgrammer.js.map +0 -1
  124. package/lib/structures/INestiaMigrateConfig.d.ts +0 -15
  125. package/lib/structures/INestiaMigrateConfig.js +0 -3
  126. package/lib/structures/INestiaMigrateConfig.js.map +0 -1
  127. package/lib/structures/INestiaMigrateContext.d.ts +0 -7
  128. package/lib/structures/INestiaMigrateContext.js +0 -3
  129. package/lib/structures/INestiaMigrateContext.js.map +0 -1
  130. package/lib/structures/INestiaMigrateController.d.ts +0 -7
  131. package/lib/structures/INestiaMigrateController.js +0 -3
  132. package/lib/structures/INestiaMigrateController.js.map +0 -1
  133. package/lib/structures/INestiaMigrateDto.d.ts +0 -7
  134. package/lib/structures/INestiaMigrateDto.js +0 -3
  135. package/lib/structures/INestiaMigrateDto.js.map +0 -1
  136. package/lib/structures/INestiaMigrateFile.d.ts +0 -5
  137. package/lib/structures/INestiaMigrateFile.js +0 -3
  138. package/lib/structures/INestiaMigrateFile.js.map +0 -1
  139. package/lib/structures/INestiaMigrateProgram.d.ts +0 -9
  140. package/lib/structures/INestiaMigrateProgram.js +0 -3
  141. package/lib/structures/INestiaMigrateProgram.js.map +0 -1
  142. package/lib/structures/INestiaMigrateSchema.d.ts +0 -4
  143. package/lib/structures/INestiaMigrateSchema.js +0 -3
  144. package/lib/structures/INestiaMigrateSchema.js.map +0 -1
  145. package/lib/utils/FilePrinter.d.ts +0 -9
  146. package/lib/utils/FilePrinter.js +0 -35
  147. package/lib/utils/FilePrinter.js.map +0 -1
  148. package/lib/utils/MapUtil.d.ts +0 -3
  149. package/lib/utils/MapUtil.js +0 -15
  150. package/lib/utils/MapUtil.js.map +0 -1
  151. package/lib/utils/SetupWizard.d.ts +0 -3
  152. package/lib/utils/SetupWizard.js +0 -18
  153. package/lib/utils/SetupWizard.js.map +0 -1
  154. package/lib/utils/StringUtil.d.ts +0 -6
  155. package/lib/utils/StringUtil.js +0 -108
  156. package/lib/utils/StringUtil.js.map +0 -1
  157. package/lib/utils/openapi-down-convert/RefVisitor.d.ts +0 -52
  158. package/lib/utils/openapi-down-convert/RefVisitor.js +0 -102
  159. package/lib/utils/openapi-down-convert/RefVisitor.js.map +0 -1
  160. package/lib/utils/openapi-down-convert/converter.d.ts +0 -146
  161. package/lib/utils/openapi-down-convert/converter.js +0 -441
  162. package/lib/utils/openapi-down-convert/converter.js.map +0 -1
@@ -1,522 +1,465 @@
1
- import { OpenApi, OpenApiTypeChecker } from "@samchon/openapi";
2
- import ts from "typescript";
3
- import typia from "typia";
4
- import { TypeFactory } from "typia/lib/factories/TypeFactory";
5
- import { FormatCheatSheet } from "typia/lib/tags/internal/FormatCheatSheet";
6
- import { Escaper } from "typia/lib/utils/Escaper";
7
-
8
- import { FilePrinter } from "../utils/FilePrinter";
9
- import { StringUtil } from "../utils/StringUtil";
10
- import { NestiaMigrateImportProgrammer } from "./NestiaMigrateImportProgrammer";
11
-
12
- export namespace NestiaMigrateSchemaProgrammer {
13
- /* -----------------------------------------------------------
14
- FACADE
15
- ----------------------------------------------------------- */
16
- export const write = (props: {
17
- components: OpenApi.IComponents;
18
- importer: NestiaMigrateImportProgrammer;
19
- schema: OpenApi.IJsonSchema;
20
- }): ts.TypeNode => {
21
- // CONSIDER ANY TYPE CASE
22
- const union: ts.TypeNode[] = [];
23
- if (OpenApiTypeChecker.isUnknown(props.schema))
24
- return TypeFactory.keyword("any");
25
-
26
- // ITERATION
27
- const type: ts.TypeNode = (() => {
28
- // ATOMIC
29
- if (OpenApiTypeChecker.isConstant(props.schema))
30
- return writeConstant({
31
- importer: props.importer,
32
- schema: props.schema,
33
- });
34
- else if (OpenApiTypeChecker.isBoolean(props.schema))
35
- return writeBoolean({
36
- importer: props.importer,
37
- schema: props.schema,
38
- });
39
- else if (OpenApiTypeChecker.isInteger(props.schema))
40
- return writeInteger({
41
- importer: props.importer,
42
- schema: props.schema,
43
- });
44
- else if (OpenApiTypeChecker.isNumber(props.schema))
45
- return writeNumber({
46
- importer: props.importer,
47
- schema: props.schema,
48
- });
49
- else if (OpenApiTypeChecker.isString(props.schema))
50
- return writeString({
51
- importer: props.importer,
52
- schema: props.schema,
53
- });
54
- // INSTANCES
55
- else if (OpenApiTypeChecker.isArray(props.schema))
56
- return writeArray({
57
- components: props.components,
58
- importer: props.importer,
59
- schema: props.schema,
60
- });
61
- else if (OpenApiTypeChecker.isTuple(props.schema))
62
- return writeTuple({
63
- components: props.components,
64
- importer: props.importer,
65
- schema: props.schema,
66
- });
67
- else if (OpenApiTypeChecker.isObject(props.schema))
68
- return writeObject({
69
- components: props.components,
70
- importer: props.importer,
71
- schema: props.schema,
72
- });
73
- else if (OpenApiTypeChecker.isReference(props.schema))
74
- return writeReference({
75
- importer: props.importer,
76
- schema: props.schema,
77
- });
78
- // UNION
79
- else if (OpenApiTypeChecker.isOneOf(props.schema))
80
- return writeUnion({
81
- components: props.components,
82
- importer: props.importer,
83
- elements: props.schema.oneOf,
84
- });
85
- else if (OpenApiTypeChecker.isNull(props.schema))
86
- return createNode("null");
87
- else return TypeFactory.keyword("any");
88
- })();
89
- union.push(type);
90
-
91
- // DETERMINE
92
- if (union.length === 0) return TypeFactory.keyword("any");
93
- else if (union.length === 1) return union[0];
94
- return ts.factory.createUnionTypeNode(union);
95
- };
96
-
97
- /* -----------------------------------------------------------
98
- ATOMICS
99
- ----------------------------------------------------------- */
100
- const writeConstant = (props: {
101
- importer: NestiaMigrateImportProgrammer;
102
- schema: OpenApi.IJsonSchema.IConstant;
103
- }): ts.TypeNode => {
104
- const intersection: ts.TypeNode[] = [
105
- ts.factory.createLiteralTypeNode(
106
- typeof props.schema.const === "boolean"
107
- ? props.schema.const === true
108
- ? ts.factory.createTrue()
109
- : ts.factory.createFalse()
110
- : typeof props.schema.const === "number"
111
- ? props.schema.const < 0
112
- ? ts.factory.createPrefixUnaryExpression(
113
- ts.SyntaxKind.MinusToken,
114
- ts.factory.createNumericLiteral(-props.schema.const),
115
- )
116
- : ts.factory.createNumericLiteral(props.schema.const)
117
- : ts.factory.createStringLiteral(props.schema.const),
118
- ),
119
- ];
120
- writePlugin({
121
- importer: props.importer,
122
- schema: props.schema,
123
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IConstant>(),
124
- intersection,
125
- });
126
- return intersection.length === 1
127
- ? intersection[0]
128
- : ts.factory.createIntersectionTypeNode(intersection);
129
- };
130
-
131
- const writeBoolean = (props: {
132
- importer: NestiaMigrateImportProgrammer;
133
- schema: OpenApi.IJsonSchema.IBoolean;
134
- }): ts.TypeNode => {
135
- const intersection: ts.TypeNode[] = [TypeFactory.keyword("boolean")];
136
- writePlugin({
137
- importer: props.importer,
138
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IBoolean>(),
139
- intersection,
140
- schema: props.schema,
141
- });
142
- return intersection.length === 1
143
- ? intersection[0]
144
- : ts.factory.createIntersectionTypeNode(intersection);
145
- };
146
-
147
- const writeInteger = (props: {
148
- importer: NestiaMigrateImportProgrammer;
149
- schema: OpenApi.IJsonSchema.IInteger;
150
- }): ts.TypeNode =>
151
- writeNumeric({
152
- factory: () => [
153
- TypeFactory.keyword("number"),
154
- props.importer.tag("Type", "int32"),
155
- ],
156
- importer: props.importer,
157
- schema: props.schema,
158
- });
159
-
160
- const writeNumber = (props: {
161
- importer: NestiaMigrateImportProgrammer;
162
- schema: OpenApi.IJsonSchema.INumber;
163
- }): ts.TypeNode =>
164
- writeNumeric({
165
- factory: () => [TypeFactory.keyword("number")],
166
- importer: props.importer,
167
- schema: props.schema,
168
- });
169
-
170
- const writeNumeric = (props: {
171
- factory: () => ts.TypeNode[];
172
- importer: NestiaMigrateImportProgrammer;
173
- schema: OpenApi.IJsonSchema.IInteger | OpenApi.IJsonSchema.INumber;
174
- }): ts.TypeNode => {
175
- const intersection: ts.TypeNode[] = props.factory();
176
- if (props.schema.default !== undefined)
177
- intersection.push(props.importer.tag("Default", props.schema.default));
178
- if (props.schema.minimum !== undefined)
179
- intersection.push(
180
- props.importer.tag(
181
- props.schema.exclusiveMinimum ? "ExclusiveMinimum" : "Minimum",
182
- props.schema.minimum,
183
- ),
184
- );
185
- if (props.schema.maximum !== undefined)
186
- intersection.push(
187
- props.importer.tag(
188
- props.schema.exclusiveMaximum ? "ExclusiveMaximum" : "Maximum",
189
- props.schema.maximum,
190
- ),
191
- );
192
- if (props.schema.multipleOf !== undefined)
193
- intersection.push(
194
- props.importer.tag("MultipleOf", props.schema.multipleOf),
195
- );
196
- writePlugin({
197
- importer: props.importer,
198
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.INumber>(),
199
- intersection,
200
- schema: props.schema,
201
- });
202
- return intersection.length === 1
203
- ? intersection[0]
204
- : ts.factory.createIntersectionTypeNode(intersection);
205
- };
206
-
207
- const writeString = (props: {
208
- importer: NestiaMigrateImportProgrammer;
209
- schema: OpenApi.IJsonSchema.IString;
210
- }): ts.TypeNode => {
211
- if (props.schema.format === "binary")
212
- return ts.factory.createTypeReferenceNode("File");
213
-
214
- const intersection: ts.TypeNode[] = [TypeFactory.keyword("string")];
215
- if (props.schema.default !== undefined)
216
- intersection.push(props.importer.tag("Default", props.schema.default));
217
- if (props.schema.minLength !== undefined)
218
- intersection.push(
219
- props.importer.tag("MinLength", props.schema.minLength),
220
- );
221
- if (props.schema.maxLength !== undefined)
222
- intersection.push(
223
- props.importer.tag("MaxLength", props.schema.maxLength),
224
- );
225
- if (props.schema.pattern !== undefined)
226
- intersection.push(props.importer.tag("Pattern", props.schema.pattern));
227
- if (
228
- props.schema.format !== undefined &&
229
- (FormatCheatSheet as Record<string, string>)[props.schema.format] !==
230
- undefined
231
- )
232
- intersection.push(props.importer.tag("Format", props.schema.format));
233
- if (props.schema.contentMediaType !== undefined)
234
- intersection.push(
235
- props.importer.tag("ContentMediaType", props.schema.contentMediaType),
236
- );
237
- writePlugin({
238
- importer: props.importer,
239
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IString>(),
240
- intersection,
241
- schema: props.schema,
242
- });
243
- return intersection.length === 1
244
- ? intersection[0]
245
- : ts.factory.createIntersectionTypeNode(intersection);
246
- };
247
-
248
- /* -----------------------------------------------------------
249
- INSTANCES
250
- ----------------------------------------------------------- */
251
- const writeArray = (props: {
252
- components: OpenApi.IComponents;
253
- importer: NestiaMigrateImportProgrammer;
254
- schema: OpenApi.IJsonSchema.IArray;
255
- }): ts.TypeNode => {
256
- const intersection: ts.TypeNode[] = [
257
- ts.factory.createArrayTypeNode(
258
- write({
259
- components: props.components,
260
- importer: props.importer,
261
- schema: props.schema.items,
262
- }),
263
- ),
264
- ];
265
- if (props.schema.minItems !== undefined)
266
- intersection.push(props.importer.tag("MinItems", props.schema.minItems));
267
- if (props.schema.maxItems !== undefined)
268
- intersection.push(props.importer.tag("MaxItems", props.schema.maxItems));
269
- if (props.schema.uniqueItems === true)
270
- intersection.push(props.importer.tag("UniqueItems"));
271
- writePlugin({
272
- importer: props.importer,
273
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IArray>(),
274
- intersection,
275
- schema: props.schema,
276
- });
277
- return intersection.length === 1
278
- ? intersection[0]
279
- : ts.factory.createIntersectionTypeNode(intersection);
280
- };
281
-
282
- const writeTuple = (props: {
283
- components: OpenApi.IComponents;
284
- importer: NestiaMigrateImportProgrammer;
285
- schema: OpenApi.IJsonSchema.ITuple;
286
- }): ts.TypeNode => {
287
- const tuple: ts.TypeNode = ts.factory.createTupleTypeNode([
288
- ...props.schema.prefixItems.map((item) =>
289
- write({
290
- components: props.components,
291
- importer: props.importer,
292
- schema: item,
293
- }),
294
- ),
295
- ...(typeof props.schema.additionalItems === "object" &&
296
- props.schema.additionalItems !== null
297
- ? [
298
- ts.factory.createRestTypeNode(
299
- write({
300
- components: props.components,
301
- importer: props.importer,
302
- schema: props.schema.additionalItems,
303
- }),
304
- ),
305
- ]
306
- : props.schema.additionalItems === true
307
- ? [
308
- ts.factory.createRestTypeNode(
309
- ts.factory.createArrayTypeNode(
310
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
311
- ),
312
- ),
313
- ]
314
- : []),
315
- ]);
316
- const intersection: ts.TypeNode[] = [tuple];
317
- writePlugin({
318
- importer: props.importer,
319
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.ITuple>(),
320
- intersection,
321
- schema: props.schema,
322
- });
323
- return intersection.length === 1
324
- ? intersection[0]
325
- : ts.factory.createIntersectionTypeNode(intersection);
326
- };
327
-
328
- const writeObject = (props: {
329
- components: OpenApi.IComponents;
330
- importer: NestiaMigrateImportProgrammer;
331
- schema: OpenApi.IJsonSchema.IObject;
332
- }): ts.TypeNode => {
333
- const regular = () =>
334
- ts.factory.createTypeLiteralNode(
335
- Object.entries(props.schema.properties ?? [])
336
- .map(([key, value], index) => [
337
- ...(index !== 0 &&
338
- (!!value.title?.length || !!value.description?.length)
339
- ? [ts.factory.createIdentifier("\n") as any]
340
- : []),
341
- writeRegularProperty({
342
- components: props.components,
343
- importer: props.importer,
344
- required: props.schema.required ?? [],
345
- key,
346
- value,
347
- }),
348
- ])
349
- .flat(),
350
- );
351
- const dynamic = () =>
352
- ts.factory.createTypeLiteralNode([
353
- writeDynamicProperty({
354
- components: props.components,
355
- importer: props.importer,
356
- schema: props.schema.additionalProperties as OpenApi.IJsonSchema,
357
- }),
358
- ]);
359
- return !!props.schema.properties?.length &&
360
- typeof props.schema.additionalProperties === "object"
361
- ? ts.factory.createIntersectionTypeNode([regular(), dynamic()])
362
- : typeof props.schema.additionalProperties === "object"
363
- ? dynamic()
364
- : regular();
365
- };
366
-
367
- const writeRegularProperty = (props: {
368
- components: OpenApi.IComponents;
369
- importer: NestiaMigrateImportProgrammer;
370
- required: string[];
371
- key: string;
372
- value: OpenApi.IJsonSchema;
373
- }) => {
374
- const valueTypeNode: ts.TypeNode = write({
375
- components: props.components,
376
- importer: props.importer,
377
- schema: props.value,
378
- });
379
- return FilePrinter.description(
380
- ts.factory.createPropertySignature(
381
- props.value.readOnly
382
- ? [ts.factory.createToken(ts.SyntaxKind.ReadonlyKeyword)]
383
- : undefined,
384
- Escaper.variable(props.key)
385
- ? ts.factory.createIdentifier(props.key)
386
- : ts.factory.createStringLiteral(props.key),
387
- props.required.includes(props.key)
388
- ? undefined
389
- : ts.factory.createToken(ts.SyntaxKind.QuestionToken),
390
- props.required.includes(props.key)
391
- ? valueTypeNode
392
- : ts.isUnionTypeNode(valueTypeNode)
393
- ? ts.factory.createUnionTypeNode([
394
- ...valueTypeNode.types,
395
- ts.factory.createTypeReferenceNode("undefined"),
396
- ])
397
- : ts.factory.createUnionTypeNode([
398
- valueTypeNode,
399
- ts.factory.createTypeReferenceNode("undefined"),
400
- ]),
401
- ),
402
- writeComment(props.value),
403
- );
404
- };
405
-
406
- const writeDynamicProperty = (props: {
407
- components: OpenApi.IComponents;
408
- importer: NestiaMigrateImportProgrammer;
409
- schema: OpenApi.IJsonSchema;
410
- }) =>
411
- FilePrinter.description(
412
- ts.factory.createIndexSignature(
413
- undefined,
414
- [
415
- ts.factory.createParameterDeclaration(
416
- undefined,
417
- undefined,
418
- ts.factory.createIdentifier("key"),
419
- undefined,
420
- TypeFactory.keyword("string"),
421
- ),
422
- ],
423
- write(props),
424
- ),
425
- writeComment(props.schema),
426
- );
427
-
428
- const writeReference = (props: {
429
- importer: NestiaMigrateImportProgrammer;
430
- schema: OpenApi.IJsonSchema.IReference;
431
- }): ts.TypeReferenceNode | ts.KeywordTypeNode => {
432
- if (props.schema.$ref.startsWith("#/components/schemas") === false)
433
- return TypeFactory.keyword("any");
434
- const name: string = props.schema.$ref
435
- .split("/")
436
- .slice(3)
437
- .filter((str) => str.length !== 0)
438
- .map(StringUtil.escapeNonVariable)
439
- .join("");
440
- if (name === "") return TypeFactory.keyword("any");
441
- return props.importer.dto(name);
442
- };
443
-
444
- /* -----------------------------------------------------------
445
- UNIONS
446
- ----------------------------------------------------------- */
447
- const writeUnion = (props: {
448
- components: OpenApi.IComponents;
449
- importer: NestiaMigrateImportProgrammer;
450
- elements: OpenApi.IJsonSchema[];
451
- }): ts.UnionTypeNode =>
452
- ts.factory.createUnionTypeNode(
453
- props.elements.map((schema) =>
454
- write({
455
- components: props.components,
456
- importer: props.importer,
457
- schema,
458
- }),
459
- ),
460
- );
461
- }
462
- const createNode = (text: string) => ts.factory.createTypeReferenceNode(text);
463
-
464
- const writeComment = (schema: OpenApi.IJsonSchema): string =>
465
- [
466
- ...(schema.description?.length
467
- ? [eraseCommentTags(schema.description)]
468
- : []),
469
- ...(schema.description?.length &&
470
- (schema.title !== undefined || schema.deprecated === true)
471
- ? [""]
472
- : []),
473
- ...(schema.title !== undefined ? [`@title ${schema.title}`] : []),
474
- ...(schema.deprecated === true ? [`@deprecated`] : []),
475
- ].join("\n");
476
-
477
- const eraseCommentTags = (description: string): string => {
478
- const lines: string[] = description.split("\n");
479
- return lines
480
- .filter((s) => COMMENT_TAGS.every((tag) => !s.includes(tag)))
481
- .join("\n");
482
- };
483
-
484
- const writePlugin = (props: {
485
- importer: NestiaMigrateImportProgrammer;
486
- regular: string[];
487
- intersection: ts.TypeNode[];
488
- schema: Record<string, any>;
489
- }) => {
490
- const extra: any = {};
491
- for (const [key, value] of Object.entries(props.schema))
492
- if (
493
- value !== undefined &&
494
- false === props.regular.includes(key) &&
495
- key.startsWith("x-")
496
- )
497
- extra[key] = value;
498
- if (Object.keys(extra).length !== 0)
499
- props.intersection.push(props.importer.tag("JsonSchemaPlugin", extra));
500
- };
501
-
502
- const COMMENT_TAGS = [
503
- // string
504
- "@format",
505
- "@pattern",
506
- "@length",
507
- "@minLength",
508
- "@maxLength",
509
- "@contentMediaType",
510
- // number
511
- "@type",
512
- "@minimum",
513
- "@maximum",
514
- "@exclusiveMinimum",
515
- "@exclusiveMaximum",
516
- "@multipleOf",
517
- // array
518
- "@items",
519
- "@minItems",
520
- "@maxItems",
521
- "@uniqueItems",
522
- ];
1
+ import { FormatCheatSheet, TypeFactory } from "@typia/core";
2
+ import { NamingConvention, OpenApiTypeChecker } from "@typia/utils";
3
+ import ts from "typescript";
4
+ import typia, { OpenApi } from "typia";
5
+
6
+ import { FilePrinter } from "../utils/FilePrinter";
7
+ import { StringUtil } from "../utils/StringUtil";
8
+ import { NestiaMigrateImportProgrammer } from "./NestiaMigrateImportProgrammer";
9
+
10
+ export namespace NestiaMigrateSchemaProgrammer {
11
+ /* -----------------------------------------------------------
12
+ FACADE
13
+ ----------------------------------------------------------- */
14
+ export const write = (props: {
15
+ components: OpenApi.IComponents;
16
+ importer: NestiaMigrateImportProgrammer;
17
+ schema: OpenApi.IJsonSchema;
18
+ }): ts.TypeNode => {
19
+ // CONSIDER ANY TYPE CASE
20
+ const union: ts.TypeNode[] = [];
21
+ if (OpenApiTypeChecker.isUnknown(props.schema))
22
+ return TypeFactory.keyword("any");
23
+
24
+ // ITERATION
25
+ const type: ts.TypeNode = (() => {
26
+ // ATOMIC
27
+ if (OpenApiTypeChecker.isConstant(props.schema))
28
+ return writeConstant({
29
+ importer: props.importer,
30
+ schema: props.schema,
31
+ });
32
+ else if (OpenApiTypeChecker.isBoolean(props.schema))
33
+ return writeBoolean();
34
+ else if (OpenApiTypeChecker.isInteger(props.schema))
35
+ return writeInteger({
36
+ importer: props.importer,
37
+ schema: props.schema,
38
+ });
39
+ else if (OpenApiTypeChecker.isNumber(props.schema))
40
+ return writeNumber({
41
+ importer: props.importer,
42
+ schema: props.schema,
43
+ });
44
+ else if (OpenApiTypeChecker.isString(props.schema))
45
+ return writeString({
46
+ importer: props.importer,
47
+ schema: props.schema,
48
+ });
49
+ // INSTANCES
50
+ else if (OpenApiTypeChecker.isArray(props.schema))
51
+ return writeArray({
52
+ components: props.components,
53
+ importer: props.importer,
54
+ schema: props.schema,
55
+ });
56
+ else if (OpenApiTypeChecker.isTuple(props.schema))
57
+ return writeTuple({
58
+ components: props.components,
59
+ importer: props.importer,
60
+ schema: props.schema,
61
+ });
62
+ else if (OpenApiTypeChecker.isObject(props.schema))
63
+ return writeObject({
64
+ components: props.components,
65
+ importer: props.importer,
66
+ schema: props.schema,
67
+ });
68
+ else if (OpenApiTypeChecker.isReference(props.schema))
69
+ return writeReference({
70
+ importer: props.importer,
71
+ schema: props.schema,
72
+ });
73
+ // UNION
74
+ else if (OpenApiTypeChecker.isOneOf(props.schema))
75
+ return writeUnion({
76
+ components: props.components,
77
+ importer: props.importer,
78
+ elements: props.schema.oneOf,
79
+ });
80
+ else if (OpenApiTypeChecker.isNull(props.schema))
81
+ return createNode("null");
82
+ else return TypeFactory.keyword("any");
83
+ })();
84
+ union.push(type);
85
+
86
+ // DETERMINE
87
+ if (union.length === 0) return TypeFactory.keyword("any");
88
+ else if (union.length === 1) return union[0]!;
89
+ return ts.factory.createUnionTypeNode(union);
90
+ };
91
+
92
+ /* -----------------------------------------------------------
93
+ ATOMICS
94
+ ----------------------------------------------------------- */
95
+ const writeConstant = (props: {
96
+ importer: NestiaMigrateImportProgrammer;
97
+ schema: OpenApi.IJsonSchema.IConstant;
98
+ }): ts.TypeNode => {
99
+ return ts.factory.createLiteralTypeNode(
100
+ typeof props.schema.const === "boolean"
101
+ ? props.schema.const === true
102
+ ? ts.factory.createTrue()
103
+ : ts.factory.createFalse()
104
+ : typeof props.schema.const === "number"
105
+ ? props.schema.const < 0
106
+ ? ts.factory.createPrefixUnaryExpression(
107
+ ts.SyntaxKind.MinusToken,
108
+ ts.factory.createNumericLiteral(-props.schema.const),
109
+ )
110
+ : ts.factory.createNumericLiteral(props.schema.const)
111
+ : ts.factory.createStringLiteral(props.schema.const),
112
+ );
113
+ };
114
+
115
+ const writeBoolean = (): ts.TypeNode => TypeFactory.keyword("boolean");
116
+
117
+ const writeInteger = (props: {
118
+ importer: NestiaMigrateImportProgrammer;
119
+ schema: OpenApi.IJsonSchema.IInteger;
120
+ }): ts.TypeNode =>
121
+ writeNumeric({
122
+ factory: () => [
123
+ TypeFactory.keyword("number"),
124
+ props.importer.tag("Type", "int32"),
125
+ ],
126
+ importer: props.importer,
127
+ schema: props.schema,
128
+ });
129
+
130
+ const writeNumber = (props: {
131
+ importer: NestiaMigrateImportProgrammer;
132
+ schema: OpenApi.IJsonSchema.INumber;
133
+ }): ts.TypeNode =>
134
+ writeNumeric({
135
+ factory: () => [TypeFactory.keyword("number")],
136
+ importer: props.importer,
137
+ schema: props.schema,
138
+ });
139
+
140
+ const writeNumeric = (props: {
141
+ factory: () => ts.TypeNode[];
142
+ importer: NestiaMigrateImportProgrammer;
143
+ schema: OpenApi.IJsonSchema.IInteger | OpenApi.IJsonSchema.INumber;
144
+ }): ts.TypeNode => {
145
+ const intersection: ts.TypeNode[] = props.factory();
146
+ if (props.schema.default !== undefined)
147
+ intersection.push(props.importer.tag("Default", props.schema.default));
148
+ if (props.schema.minimum !== undefined)
149
+ intersection.push(
150
+ props.importer.tag(
151
+ props.schema.exclusiveMinimum ? "ExclusiveMinimum" : "Minimum",
152
+ props.schema.minimum,
153
+ ),
154
+ );
155
+ if (props.schema.maximum !== undefined)
156
+ intersection.push(
157
+ props.importer.tag(
158
+ props.schema.exclusiveMaximum ? "ExclusiveMaximum" : "Maximum",
159
+ props.schema.maximum,
160
+ ),
161
+ );
162
+ if (props.schema.multipleOf !== undefined)
163
+ intersection.push(
164
+ props.importer.tag("MultipleOf", props.schema.multipleOf),
165
+ );
166
+ return intersection.length === 1
167
+ ? intersection[0]!
168
+ : ts.factory.createIntersectionTypeNode(intersection);
169
+ };
170
+
171
+ const writeString = (props: {
172
+ importer: NestiaMigrateImportProgrammer;
173
+ schema: OpenApi.IJsonSchema.IString;
174
+ }): ts.TypeNode => {
175
+ if (props.schema.format === "binary")
176
+ return ts.factory.createTypeReferenceNode("File");
177
+
178
+ const intersection: ts.TypeNode[] = [TypeFactory.keyword("string")];
179
+ if (props.schema.default !== undefined)
180
+ intersection.push(props.importer.tag("Default", props.schema.default));
181
+ if (props.schema.minLength !== undefined)
182
+ intersection.push(
183
+ props.importer.tag("MinLength", props.schema.minLength),
184
+ );
185
+ if (props.schema.maxLength !== undefined)
186
+ intersection.push(
187
+ props.importer.tag("MaxLength", props.schema.maxLength),
188
+ );
189
+ if (props.schema.pattern !== undefined)
190
+ intersection.push(props.importer.tag("Pattern", props.schema.pattern));
191
+ if (
192
+ props.schema.format !== undefined &&
193
+ (FormatCheatSheet as Record<string, string>)[props.schema.format] !==
194
+ undefined
195
+ )
196
+ intersection.push(props.importer.tag("Format", props.schema.format));
197
+ if (props.schema.contentMediaType !== undefined)
198
+ intersection.push(
199
+ props.importer.tag("ContentMediaType", props.schema.contentMediaType),
200
+ );
201
+ return intersection.length === 1
202
+ ? intersection[0]!
203
+ : ts.factory.createIntersectionTypeNode(intersection);
204
+ };
205
+
206
+ /* -----------------------------------------------------------
207
+ INSTANCES
208
+ ----------------------------------------------------------- */
209
+ const writeArray = (props: {
210
+ components: OpenApi.IComponents;
211
+ importer: NestiaMigrateImportProgrammer;
212
+ schema: OpenApi.IJsonSchema.IArray;
213
+ }): ts.TypeNode => {
214
+ const intersection: ts.TypeNode[] = [
215
+ ts.factory.createArrayTypeNode(
216
+ write({
217
+ components: props.components,
218
+ importer: props.importer,
219
+ schema: props.schema.items,
220
+ }),
221
+ ),
222
+ ];
223
+ if (props.schema.minItems !== undefined)
224
+ intersection.push(props.importer.tag("MinItems", props.schema.minItems));
225
+ if (props.schema.maxItems !== undefined)
226
+ intersection.push(props.importer.tag("MaxItems", props.schema.maxItems));
227
+ if (props.schema.uniqueItems === true)
228
+ intersection.push(props.importer.tag("UniqueItems"));
229
+ return intersection.length === 1
230
+ ? intersection[0]!
231
+ : ts.factory.createIntersectionTypeNode(intersection);
232
+ };
233
+
234
+ const writeTuple = (props: {
235
+ components: OpenApi.IComponents;
236
+ importer: NestiaMigrateImportProgrammer;
237
+ schema: OpenApi.IJsonSchema.ITuple;
238
+ }): ts.TypeNode =>
239
+ ts.factory.createTupleTypeNode([
240
+ ...props.schema.prefixItems.map((item) =>
241
+ write({
242
+ components: props.components,
243
+ importer: props.importer,
244
+ schema: item,
245
+ }),
246
+ ),
247
+ ...(typeof props.schema.additionalItems === "object" &&
248
+ props.schema.additionalItems !== null
249
+ ? [
250
+ ts.factory.createRestTypeNode(
251
+ write({
252
+ components: props.components,
253
+ importer: props.importer,
254
+ schema: props.schema.additionalItems,
255
+ }),
256
+ ),
257
+ ]
258
+ : props.schema.additionalItems === true
259
+ ? [
260
+ ts.factory.createRestTypeNode(
261
+ ts.factory.createArrayTypeNode(
262
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
263
+ ),
264
+ ),
265
+ ]
266
+ : []),
267
+ ]);
268
+
269
+ const writeObject = (props: {
270
+ components: OpenApi.IComponents;
271
+ importer: NestiaMigrateImportProgrammer;
272
+ schema: OpenApi.IJsonSchema.IObject;
273
+ }): ts.TypeNode => {
274
+ const regular = () =>
275
+ ts.factory.createTypeLiteralNode(
276
+ Object.entries(props.schema.properties ?? [])
277
+ .map(([key, value], index) => [
278
+ ...(index !== 0 &&
279
+ (!!value.title?.length || !!value.description?.length)
280
+ ? [ts.factory.createIdentifier("\n") as any]
281
+ : []),
282
+ writeRegularProperty({
283
+ components: props.components,
284
+ importer: props.importer,
285
+ required: props.schema.required ?? [],
286
+ key,
287
+ value,
288
+ }),
289
+ ])
290
+ .flat(),
291
+ );
292
+ const dynamic = () =>
293
+ ts.factory.createTypeLiteralNode([
294
+ writeDynamicProperty({
295
+ components: props.components,
296
+ importer: props.importer,
297
+ schema: props.schema.additionalProperties as OpenApi.IJsonSchema,
298
+ }),
299
+ ]);
300
+ return !!props.schema.properties?.length &&
301
+ typeof props.schema.additionalProperties === "object"
302
+ ? ts.factory.createIntersectionTypeNode([regular(), dynamic()])
303
+ : typeof props.schema.additionalProperties === "object"
304
+ ? dynamic()
305
+ : regular();
306
+ };
307
+
308
+ const writeRegularProperty = (props: {
309
+ components: OpenApi.IComponents;
310
+ importer: NestiaMigrateImportProgrammer;
311
+ required: string[];
312
+ key: string;
313
+ value: OpenApi.IJsonSchema;
314
+ }) => {
315
+ const valueTypeNode: ts.TypeNode = write({
316
+ components: props.components,
317
+ importer: props.importer,
318
+ schema: props.value,
319
+ });
320
+ return FilePrinter.description(
321
+ ts.factory.createPropertySignature(
322
+ props.value.readOnly
323
+ ? [ts.factory.createToken(ts.SyntaxKind.ReadonlyKeyword)]
324
+ : undefined,
325
+ NamingConvention.variable(props.key)
326
+ ? ts.factory.createIdentifier(props.key)
327
+ : ts.factory.createStringLiteral(props.key),
328
+ props.required.includes(props.key)
329
+ ? undefined
330
+ : ts.factory.createToken(ts.SyntaxKind.QuestionToken),
331
+ props.required.includes(props.key)
332
+ ? valueTypeNode
333
+ : ts.isUnionTypeNode(valueTypeNode)
334
+ ? ts.factory.createUnionTypeNode([
335
+ ...valueTypeNode.types,
336
+ ts.factory.createTypeReferenceNode("undefined"),
337
+ ])
338
+ : ts.factory.createUnionTypeNode([
339
+ valueTypeNode,
340
+ ts.factory.createTypeReferenceNode("undefined"),
341
+ ]),
342
+ ),
343
+ writeComment(props.value),
344
+ );
345
+ };
346
+
347
+ const writeDynamicProperty = (props: {
348
+ components: OpenApi.IComponents;
349
+ importer: NestiaMigrateImportProgrammer;
350
+ schema: OpenApi.IJsonSchema;
351
+ }) =>
352
+ FilePrinter.description(
353
+ ts.factory.createIndexSignature(
354
+ undefined,
355
+ [
356
+ ts.factory.createParameterDeclaration(
357
+ undefined,
358
+ undefined,
359
+ ts.factory.createIdentifier("key"),
360
+ undefined,
361
+ TypeFactory.keyword("string"),
362
+ ),
363
+ ],
364
+ write(props),
365
+ ),
366
+ writeComment(props.schema),
367
+ );
368
+
369
+ const writeReference = (props: {
370
+ importer: NestiaMigrateImportProgrammer;
371
+ schema: OpenApi.IJsonSchema.IReference;
372
+ }): ts.TypeReferenceNode | ts.KeywordTypeNode => {
373
+ if (props.schema.$ref.startsWith("#/components/schemas") === false)
374
+ return TypeFactory.keyword("any");
375
+ const name: string = props.schema.$ref
376
+ .split("/")
377
+ .slice(3)
378
+ .filter((str) => str.length !== 0)
379
+ .map(StringUtil.escapeNonVariable)
380
+ .join("");
381
+ if (name === "") return TypeFactory.keyword("any");
382
+ return props.importer.dto(name);
383
+ };
384
+
385
+ /* -----------------------------------------------------------
386
+ UNIONS
387
+ ----------------------------------------------------------- */
388
+ const writeUnion = (props: {
389
+ components: OpenApi.IComponents;
390
+ importer: NestiaMigrateImportProgrammer;
391
+ elements: OpenApi.IJsonSchema[];
392
+ }): ts.UnionTypeNode =>
393
+ ts.factory.createUnionTypeNode(
394
+ props.elements.map((schema) =>
395
+ write({
396
+ components: props.components,
397
+ importer: props.importer,
398
+ schema,
399
+ }),
400
+ ),
401
+ );
402
+ }
403
+ const createNode = (text: string) => ts.factory.createTypeReferenceNode(text);
404
+
405
+ const writeComment = (schema: OpenApi.IJsonSchema): string => {
406
+ interface IPlugin {
407
+ key: string;
408
+ value: undefined | string | number | boolean;
409
+ }
410
+ const plugins: IPlugin[] = [];
411
+ if (schema.title !== undefined)
412
+ plugins.push({
413
+ key: "title",
414
+ value: schema.title,
415
+ });
416
+ if (schema.deprecated === true)
417
+ plugins.push({
418
+ key: "deprecated",
419
+ value: undefined,
420
+ });
421
+ for (const [key, value] of Object.entries(schema))
422
+ if (key.startsWith("x-") && typia.is<boolean | number | string>(value))
423
+ plugins.push({
424
+ key,
425
+ value,
426
+ });
427
+ return [
428
+ ...(schema.description?.length
429
+ ? [eraseCommentTags(schema.description)]
430
+ : []),
431
+ ...(schema.description?.length && plugins.length !== 0 ? [""] : []),
432
+ ...plugins.map((p) =>
433
+ p.value === undefined ? `@${p.key}` : `@${p.key} ${String(p.value)}`,
434
+ ),
435
+ ].join("\n");
436
+ };
437
+
438
+ const eraseCommentTags = (description: string): string => {
439
+ const lines: string[] = description.split("\n");
440
+ return lines
441
+ .filter((s) => COMMENT_TAGS.every((tag) => !s.includes(tag)))
442
+ .join("\n");
443
+ };
444
+
445
+ const COMMENT_TAGS = [
446
+ // string
447
+ "@format",
448
+ "@pattern",
449
+ "@length",
450
+ "@minLength",
451
+ "@maxLength",
452
+ "@contentMediaType",
453
+ // number
454
+ "@type",
455
+ "@minimum",
456
+ "@maximum",
457
+ "@exclusiveMinimum",
458
+ "@exclusiveMaximum",
459
+ "@multipleOf",
460
+ // array
461
+ "@items",
462
+ "@minItems",
463
+ "@maxItems",
464
+ "@uniqueItems",
465
+ ];