typia 3.4.13 → 3.4.14

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 (157) hide show
  1. package/README.md +9 -8
  2. package/lib/factories/internal/iterate_metadata_object.js +4 -2
  3. package/lib/factories/internal/iterate_metadata_object.js.map +1 -1
  4. package/package.json +1 -1
  5. package/src/IValidation.ts +21 -21
  6. package/src/Primitive.ts +82 -82
  7. package/src/TypeGuardError.ts +36 -36
  8. package/src/executable/internal/CommandParser.ts +15 -15
  9. package/src/executable/internal/TypiaSetupWizard.ts +175 -175
  10. package/src/executable/typia.ts +46 -46
  11. package/src/factories/CommentFactory.ts +10 -10
  12. package/src/factories/ExpressionFactory.ts +66 -66
  13. package/src/factories/IdentifierFactory.ts +72 -72
  14. package/src/factories/LiteralFactory.ts +44 -44
  15. package/src/factories/MetadataCollection.ts +122 -122
  16. package/src/factories/MetadataFactory.ts +46 -46
  17. package/src/factories/MetadataTagFactory.ts +347 -347
  18. package/src/factories/StatementFactory.ts +60 -60
  19. package/src/factories/TemplateFactory.ts +56 -56
  20. package/src/factories/TypeFactory.ts +101 -101
  21. package/src/factories/ValueFactory.ts +12 -12
  22. package/src/factories/internal/MetadataHelper.ts +12 -12
  23. package/src/factories/internal/emplace_metadata_object.ts +140 -140
  24. package/src/factories/internal/explore_metadata.ts +91 -91
  25. package/src/factories/internal/iterate_metadata.ts +80 -80
  26. package/src/factories/internal/iterate_metadata_array.ts +29 -29
  27. package/src/factories/internal/iterate_metadata_atomic.ts +59 -59
  28. package/src/factories/internal/iterate_metadata_coalesce.ts +33 -33
  29. package/src/factories/internal/iterate_metadata_constant.ts +58 -58
  30. package/src/factories/internal/iterate_metadata_map.ts +41 -41
  31. package/src/factories/internal/iterate_metadata_native.ts +227 -227
  32. package/src/factories/internal/iterate_metadata_object.ts +48 -45
  33. package/src/factories/internal/iterate_metadata_resolve.ts +27 -27
  34. package/src/factories/internal/iterate_metadata_set.ts +33 -33
  35. package/src/factories/internal/iterate_metadata_template.ts +38 -38
  36. package/src/factories/internal/iterate_metadata_tuple.ts +45 -45
  37. package/src/factories/internal/iterate_metadata_union.ts +59 -59
  38. package/src/functional/$every.ts +11 -11
  39. package/src/functional/$guard.ts +35 -35
  40. package/src/functional/$is_email.ts +5 -5
  41. package/src/functional/$is_ipv4.ts +5 -5
  42. package/src/functional/$is_ipv6.ts +5 -5
  43. package/src/functional/$is_url.ts +5 -5
  44. package/src/functional/$is_uuid.ts +5 -5
  45. package/src/functional/$join.ts +50 -50
  46. package/src/functional/$number.ts +19 -19
  47. package/src/functional/$report.ts +15 -15
  48. package/src/functional/$rest.ts +3 -3
  49. package/src/functional/$string.ts +37 -37
  50. package/src/functional/$tail.ts +6 -6
  51. package/src/index.ts +4 -4
  52. package/src/metadata/IJsDocTagInfo.ts +10 -10
  53. package/src/metadata/IMetadata.ts +25 -25
  54. package/src/metadata/IMetadataApplication.ts +7 -7
  55. package/src/metadata/IMetadataConstant.ts +16 -16
  56. package/src/metadata/IMetadataEntry.ts +6 -6
  57. package/src/metadata/IMetadataObject.ts +29 -29
  58. package/src/metadata/IMetadataProperty.ts +11 -11
  59. package/src/metadata/IMetadataTag.ts +122 -122
  60. package/src/metadata/Metadata.ts +477 -477
  61. package/src/metadata/MetadataConstant.ts +3 -3
  62. package/src/metadata/MetadataObject.ts +131 -131
  63. package/src/metadata/MetadataProperty.ts +64 -64
  64. package/src/module.ts +1535 -1535
  65. package/src/programmers/ApplicationProgrammer.ts +55 -55
  66. package/src/programmers/AssertParseProgrammer.ts +45 -45
  67. package/src/programmers/AssertProgrammer.ts +444 -444
  68. package/src/programmers/AssertStringifyProgrammer.ts +45 -45
  69. package/src/programmers/CheckerProgrammer.ts +804 -804
  70. package/src/programmers/FeatureProgrammer.ts +327 -327
  71. package/src/programmers/IsParseProgrammer.ts +51 -51
  72. package/src/programmers/IsProgrammer.ts +172 -172
  73. package/src/programmers/IsStringifyProgrammer.ts +49 -49
  74. package/src/programmers/StringifyProgrammer.ts +756 -756
  75. package/src/programmers/ValidateParseProgrammer.ts +49 -49
  76. package/src/programmers/ValidateProgrammer.ts +236 -236
  77. package/src/programmers/ValidateStringifyProgrammer.ts +60 -60
  78. package/src/programmers/helpers/AtomicPredicator.ts +15 -15
  79. package/src/programmers/helpers/FunctionImporeter.ts +31 -31
  80. package/src/programmers/helpers/IExpressionEntry.ts +10 -10
  81. package/src/programmers/helpers/OptionPredicator.ts +11 -11
  82. package/src/programmers/helpers/StringifyJoinder.ts +111 -111
  83. package/src/programmers/helpers/StringifyPredicator.ts +18 -18
  84. package/src/programmers/helpers/UnionExplorer.ts +437 -437
  85. package/src/programmers/helpers/UnionPredicator.ts +81 -81
  86. package/src/programmers/internal/application_array.ts +45 -45
  87. package/src/programmers/internal/application_boolean.ts +17 -17
  88. package/src/programmers/internal/application_constant.ts +29 -29
  89. package/src/programmers/internal/application_default.ts +17 -17
  90. package/src/programmers/internal/application_default_string.ts +32 -32
  91. package/src/programmers/internal/application_native.ts +29 -29
  92. package/src/programmers/internal/application_number.ts +76 -76
  93. package/src/programmers/internal/application_object.ts +103 -103
  94. package/src/programmers/internal/application_schema.ts +221 -221
  95. package/src/programmers/internal/application_string.ts +49 -49
  96. package/src/programmers/internal/application_templates.ts +27 -27
  97. package/src/programmers/internal/application_tuple.ts +25 -25
  98. package/src/programmers/internal/check_array.ts +44 -44
  99. package/src/programmers/internal/check_dynamic_properties.ts +146 -146
  100. package/src/programmers/internal/check_everything.ts +25 -25
  101. package/src/programmers/internal/check_length.ts +46 -46
  102. package/src/programmers/internal/check_native.ts +9 -9
  103. package/src/programmers/internal/check_number.ts +178 -178
  104. package/src/programmers/internal/check_object.ts +42 -42
  105. package/src/programmers/internal/check_string.ts +24 -24
  106. package/src/programmers/internal/check_string_tags.ts +63 -63
  107. package/src/programmers/internal/check_template.ts +50 -50
  108. package/src/programmers/internal/decode_union_object.ts +73 -73
  109. package/src/programmers/internal/feature_object_entries.ts +49 -49
  110. package/src/programmers/internal/metadata_to_pattern.ts +31 -31
  111. package/src/programmers/internal/stringify_dynamic_properties.ts +164 -164
  112. package/src/programmers/internal/stringify_native.ts +8 -8
  113. package/src/programmers/internal/stringify_regular_properties.ts +81 -81
  114. package/src/programmers/internal/template_to_pattern.ts +15 -15
  115. package/src/schemas/IJsonApplication.ts +9 -9
  116. package/src/schemas/IJsonComponents.ts +24 -24
  117. package/src/schemas/IJsonSchema.ts +92 -92
  118. package/src/transform.ts +20 -20
  119. package/src/transformers/CallExpressionTransformer.ts +124 -124
  120. package/src/transformers/ExpressionWithArgumentTransformer.ts +66 -66
  121. package/src/transformers/FileTransformer.ts +49 -49
  122. package/src/transformers/IProject.ts +11 -11
  123. package/src/transformers/ITransformOptions.ts +4 -4
  124. package/src/transformers/NodeTransformer.ts +19 -19
  125. package/src/transformers/features/miscellaneous/ApplicationTransformer.ts +114 -114
  126. package/src/transformers/features/miscellaneous/CreateInstanceTransformer.ts +41 -41
  127. package/src/transformers/features/miscellaneous/MetadataTransformer.ts +55 -55
  128. package/src/transformers/features/parsers/AssertParseTransformer.ts +36 -36
  129. package/src/transformers/features/parsers/CreateAssertParseTransformer.ts +32 -32
  130. package/src/transformers/features/parsers/CreateIsParseTransformer.ts +32 -32
  131. package/src/transformers/features/parsers/CreateValidateParseTransformer.ts +32 -32
  132. package/src/transformers/features/parsers/IsParseTransformer.ts +36 -36
  133. package/src/transformers/features/parsers/ValidateParseTransformer.ts +36 -36
  134. package/src/transformers/features/stringifiers/AssertStringifyTransformer.ts +38 -38
  135. package/src/transformers/features/stringifiers/CreateAssertStringifyTransformer.ts +32 -32
  136. package/src/transformers/features/stringifiers/CreateIsStringifyTransformer.ts +32 -32
  137. package/src/transformers/features/stringifiers/CreateStringifyTransformer.ts +31 -31
  138. package/src/transformers/features/stringifiers/CreateValidateStringifyProgrammer.ts +32 -32
  139. package/src/transformers/features/stringifiers/IsStringifyTransformer.ts +38 -38
  140. package/src/transformers/features/stringifiers/StringifyTransformer.ts +36 -36
  141. package/src/transformers/features/stringifiers/ValidateStringifyTransformer.ts +38 -38
  142. package/src/transformers/features/validators/AssertTransformer.ts +43 -43
  143. package/src/transformers/features/validators/CreateAssertTransformer.ts +35 -35
  144. package/src/transformers/features/validators/CreateIsTransformer.ts +35 -35
  145. package/src/transformers/features/validators/CreateValidateTransformer.ts +35 -35
  146. package/src/transformers/features/validators/IsTransformer.ts +43 -43
  147. package/src/transformers/features/validators/ValidateTransformer.ts +43 -43
  148. package/src/typings/Atomic.ts +17 -17
  149. package/src/typings/ClassProperties.ts +5 -5
  150. package/src/typings/OmitNever.ts +3 -3
  151. package/src/typings/SpecialFields.ts +3 -3
  152. package/src/typings/Writable.ts +11 -11
  153. package/src/utils/ArrayUtil.ts +49 -49
  154. package/src/utils/Escaper.ts +50 -50
  155. package/src/utils/MapUtil.ts +14 -14
  156. package/src/utils/PatternUtil.ts +30 -30
  157. package/src/utils/Singleton.ts +17 -17
@@ -1,347 +1,347 @@
1
- import ts from "typescript";
2
-
3
- import { IMetadataTag } from "../metadata/IMetadataTag";
4
- import { Metadata } from "../metadata/Metadata";
5
-
6
- export namespace MetadataTagFactory {
7
- export function generate(
8
- identifier: () => string,
9
- metadata: Metadata,
10
- tagList: ts.JSDocTagInfo[],
11
- ): IMetadataTag[] {
12
- const output: IMetadataTag[] = [];
13
- for (const tag of tagList) {
14
- const elem: IMetadataTag | null = parse(
15
- identifier,
16
- metadata,
17
- tag,
18
- output,
19
- );
20
- if (elem !== null) output.push(elem);
21
- }
22
- return output;
23
- }
24
-
25
- function parse(
26
- identifier: () => string,
27
- metadata: Metadata,
28
- tag: ts.JSDocTagInfo,
29
- output: IMetadataTag[],
30
- ): IMetadataTag | null {
31
- const closure = PARSER[tag.name];
32
- if (closure === undefined) return null;
33
-
34
- const text = (tag.text || [])[0]?.text;
35
- if (text === undefined)
36
- throw new Error(`${LABEL}: no tag value on ${identifier()}`);
37
-
38
- return closure(identifier, metadata, text, output);
39
- }
40
- }
41
-
42
- const PARSER: Record<
43
- string,
44
- (
45
- identifier: () => string,
46
- metadata: Metadata,
47
- text: string,
48
- output: IMetadataTag[],
49
- ) => IMetadataTag | null
50
- > = {
51
- /* -----------------------------------------------------------
52
- ARRAY
53
- ----------------------------------------------------------- */
54
- items: (identifier, metadata, text, output) => {
55
- validate(identifier, metadata, output, "items", "array", [
56
- "minItems",
57
- "maxItems",
58
- ]);
59
- return parse_range("items", identifier, text, true);
60
- },
61
- minItems: (identifier, metadata, text, output) => {
62
- validate(identifier, metadata, output, "minItems", "array", ["items"]);
63
- return {
64
- kind: "minItems",
65
- value: parse_number(identifier, text),
66
- };
67
- },
68
- maxItems: (identifier, metadata, text, output) => {
69
- validate(identifier, metadata, output, "maxItems", "array", ["items"]);
70
- return {
71
- kind: "maxItems",
72
- value: parse_number(identifier, text),
73
- };
74
- },
75
-
76
- /* -----------------------------------------------------------
77
- NUMBER
78
- ----------------------------------------------------------- */
79
- type: (identifier, metadata, text, output) => {
80
- validate(identifier, metadata, output, "type", "number", []);
81
- if (text !== "int" && text !== "uint")
82
- throw new Error(`${LABEL}: invalid type tag on "${identifier()}".`);
83
- return { kind: "type", value: text };
84
- },
85
- range: (identifier, metadata, text, output) => {
86
- validate(identifier, metadata, output, "range", "number", [
87
- "exclusiveMinimum",
88
- "minimum",
89
- "maximum",
90
- "exclusiveMaximum",
91
- ]);
92
- return parse_range("range", identifier, text, false);
93
- },
94
- minimum: (identifier, metadata, text, output) => {
95
- validate(identifier, metadata, output, "minimum", "number", [
96
- "range",
97
- "exclusiveMaximum",
98
- "exclusiveMinimum",
99
- ]);
100
- return {
101
- kind: "minimum",
102
- value: parse_number(identifier, text),
103
- };
104
- },
105
- maximum: (identifier, metadata, text, output) => {
106
- validate(identifier, metadata, output, "maximum", "number", [
107
- "range",
108
- "exclusiveMinimum",
109
- "exclusiveMaximum",
110
- ]);
111
- return {
112
- kind: "maximum",
113
- value: parse_number(identifier, text),
114
- };
115
- },
116
- exclusiveMinimum: (identifier, metadata, text, output) => {
117
- validate(identifier, metadata, output, "exclusiveMinimum", "number", [
118
- "range",
119
- "minimum",
120
- "maximum",
121
- ]);
122
- return {
123
- kind: "exclusiveMinimum",
124
- value: parse_number(identifier, text),
125
- };
126
- },
127
- exclusiveMaximum: (identifier, metadata, text, output) => {
128
- validate(identifier, metadata, output, "exclusiveMaximum", "number", [
129
- "range",
130
- "minimum",
131
- "maximum",
132
- ]);
133
- return {
134
- kind: "exclusiveMaximum",
135
- value: parse_number(identifier, text),
136
- };
137
- },
138
- multipleOf: (identifier, metadata, text, output) => {
139
- validate(identifier, metadata, output, "multipleOf", "number", [
140
- "step",
141
- ]);
142
- return {
143
- kind: "multipleOf",
144
- value: parse_number(identifier, text),
145
- };
146
- },
147
- step: (identifier, metadata, text, output) => {
148
- validate(identifier, metadata, output, "step", "number", [
149
- "multipleOf",
150
- ]);
151
-
152
- const minimum: boolean = output.some(
153
- (tag) =>
154
- tag.kind === "minimum" ||
155
- tag.kind === "exclusiveMinimum" ||
156
- (tag.kind === "range" && tag.minimum !== undefined),
157
- );
158
- if (minimum === undefined)
159
- throw new Error(
160
- `${LABEL}: step requires minimum tag on "${identifier()}".`,
161
- );
162
-
163
- return {
164
- kind: "step",
165
- value: parse_number(identifier, text),
166
- };
167
- },
168
-
169
- /* -----------------------------------------------------------
170
- STRING
171
- ----------------------------------------------------------- */
172
- format: (identifier, metadata, value, output) => {
173
- validate(identifier, metadata, output, "format", "string", ["pattern"]);
174
-
175
- // Ignore arbitrary @format values in the internal metadata,
176
- // these are currently only supported on the typia.application() API.
177
- if (FORMATS.has(value) === false) return null;
178
-
179
- return {
180
- kind: "format",
181
- value: value as "uuid",
182
- };
183
- },
184
- pattern: (identifier, metadata, value, output) => {
185
- validate(identifier, metadata, output, "pattern", "string", ["format"]);
186
- return {
187
- kind: "pattern",
188
- value,
189
- };
190
- },
191
- length: (identifier, metadata, text, output) => {
192
- validate(identifier, metadata, output, "length", "string", [
193
- "minLength",
194
- "maxLength",
195
- ]);
196
- return parse_range("length", identifier, text, true);
197
- },
198
- minLength: (identifier, metadata, text, output) => {
199
- validate(identifier, metadata, output, "minLength", "string", [
200
- "length",
201
- ]);
202
- return {
203
- kind: "minLength",
204
- value: parse_number(identifier, text),
205
- };
206
- },
207
- maxLength: (identifier, metadata, text, output) => {
208
- validate(identifier, metadata, output, "maxLength", "string", [
209
- "length",
210
- ]);
211
- return {
212
- kind: "maxLength",
213
- value: parse_number(identifier, text),
214
- };
215
- },
216
- };
217
-
218
- function parse_range<Kind extends string>(
219
- kind: Kind,
220
- identifier: () => string,
221
- text: string,
222
- allowScalar: boolean,
223
- ): Omit<IMetadataTag.IRange, "kind"> & { kind: Kind } {
224
- if (allowScalar === true && Number.isNaN(Number(text)) === false) {
225
- const value: number = Number(text);
226
- if (Math.floor(value) !== value)
227
- throw new Error(`${LABEL}: invalid length on "${identifier()}".`);
228
- return {
229
- kind,
230
- minimum: {
231
- include: true,
232
- value,
233
- },
234
- maximum: {
235
- include: true,
236
- value,
237
- },
238
- };
239
- } else if (text.indexOf(",") === -1)
240
- if (LEFT_PARENTHESIS.some((str) => text.indexOf(str) !== -1))
241
- return {
242
- kind,
243
- minimum: parse_side("left")(kind)(identifier)(text),
244
- };
245
- else if (RIGHT_PARENTHESIS.some((str) => text.indexOf(str) !== -1))
246
- return {
247
- kind,
248
- maximum: parse_side("right")(kind)(identifier)(text),
249
- };
250
- else
251
- throw new Error(
252
- `${LABEL}: invalid ${kind} tag on "${identifier()}".`,
253
- );
254
-
255
- const [left, right] = text.split(",") as [string, string];
256
- return {
257
- kind,
258
- minimum: parse_side("left")(kind)(identifier)(left),
259
- maximum: parse_side("right")(kind)(identifier)(right),
260
- };
261
- }
262
-
263
- const parse_side = (side: "left" | "right") => {
264
- const symbol = side === "left" ? LEFT_PARENTHESIS : RIGHT_PARENTHESIS;
265
- const substring: (str: string, index: number) => string =
266
- side === "left"
267
- ? (str, index) => str.substring(index + 1)
268
- : (str, index) => str.substring(0, index);
269
- return (tag: string) => (identifier: () => string) => (text: string) => {
270
- const [index, include] =
271
- text.indexOf(symbol[0]) !== -1
272
- ? [text.indexOf(symbol[0]), true]
273
- : [text.indexOf(symbol[1]), false];
274
- if (index === -1)
275
- throw new Error(
276
- `${LABEL}: invalid ${tag} tag on "${identifier()}".`,
277
- );
278
- return {
279
- include,
280
- value: parse_number(identifier, substring(text, index)),
281
- };
282
- };
283
- };
284
-
285
- function parse_number(identifier: () => string, str: string): number {
286
- const value: number = Number(str);
287
- if (isNaN(value) === true)
288
- throw new Error(`${LABEL}: invalid number on "${identifier()}".`);
289
- return value;
290
- }
291
-
292
- const LABEL = "Error on typia.MetadataTagFactory.generate()";
293
- const LEFT_PARENTHESIS = ["[", "("] as const;
294
- const RIGHT_PARENTHESIS = ["]", ")"] as const;
295
- const FORMATS = new Set(["uuid", "email", "url", "mobile", "ipv4", "ipv6"]);
296
-
297
- const WRONG_TYPE = (
298
- tag: string,
299
- type: "string" | "number" | "array",
300
- identifier: () => string,
301
- ) => `${LABEL}: ${tag} requires ${type} type, but no "${identifier()}".`;
302
-
303
- function validate(
304
- identifier: () => string,
305
- metadata: Metadata,
306
- output: IMetadataTag[],
307
- kind: IMetadataTag["kind"],
308
- type: "array" | "string" | "number",
309
- neighbors: IMetadataTag["kind"][],
310
- ): void {
311
- // TYPE CHECKING
312
- if (type === "array") {
313
- if (has_array(metadata) === false)
314
- throw new Error(WRONG_TYPE(kind, "array", identifier));
315
- } else if (has_atomic(metadata, type) === false)
316
- throw new Error(WRONG_TYPE(kind, type, identifier));
317
-
318
- // DUPLICATED TAG
319
- if (output.some((tag) => tag.kind === kind))
320
- throw new Error(
321
- `${LABEL}: duplicated ${kind} tags on "${identifier()}".`,
322
- );
323
-
324
- // NEIGHBOR TAG
325
- for (const name of neighbors)
326
- if (output.some((tag) => tag.kind === name))
327
- throw new Error(
328
- `${LABEL}: ${kind} and ${name} tags on "${identifier()}".`,
329
- );
330
- }
331
-
332
- function has_atomic(metadata: Metadata, type: "string" | "number"): boolean {
333
- return (
334
- metadata.atomics.find((atom) => atom === type) !== undefined ||
335
- metadata.arrays.some((child) => has_atomic(child, type)) ||
336
- metadata.tuples.some((tuple) =>
337
- tuple.some((child) => has_atomic(child, type)),
338
- )
339
- );
340
- }
341
-
342
- function has_array(metadata: Metadata): boolean {
343
- return (
344
- metadata.arrays.length !== 0 ||
345
- metadata.tuples.some((tuple) => tuple.some((child) => has_array(child)))
346
- );
347
- }
1
+ import ts from "typescript";
2
+
3
+ import { IMetadataTag } from "../metadata/IMetadataTag";
4
+ import { Metadata } from "../metadata/Metadata";
5
+
6
+ export namespace MetadataTagFactory {
7
+ export function generate(
8
+ identifier: () => string,
9
+ metadata: Metadata,
10
+ tagList: ts.JSDocTagInfo[],
11
+ ): IMetadataTag[] {
12
+ const output: IMetadataTag[] = [];
13
+ for (const tag of tagList) {
14
+ const elem: IMetadataTag | null = parse(
15
+ identifier,
16
+ metadata,
17
+ tag,
18
+ output,
19
+ );
20
+ if (elem !== null) output.push(elem);
21
+ }
22
+ return output;
23
+ }
24
+
25
+ function parse(
26
+ identifier: () => string,
27
+ metadata: Metadata,
28
+ tag: ts.JSDocTagInfo,
29
+ output: IMetadataTag[],
30
+ ): IMetadataTag | null {
31
+ const closure = PARSER[tag.name];
32
+ if (closure === undefined) return null;
33
+
34
+ const text = (tag.text || [])[0]?.text;
35
+ if (text === undefined)
36
+ throw new Error(`${LABEL}: no tag value on ${identifier()}`);
37
+
38
+ return closure(identifier, metadata, text, output);
39
+ }
40
+ }
41
+
42
+ const PARSER: Record<
43
+ string,
44
+ (
45
+ identifier: () => string,
46
+ metadata: Metadata,
47
+ text: string,
48
+ output: IMetadataTag[],
49
+ ) => IMetadataTag | null
50
+ > = {
51
+ /* -----------------------------------------------------------
52
+ ARRAY
53
+ ----------------------------------------------------------- */
54
+ items: (identifier, metadata, text, output) => {
55
+ validate(identifier, metadata, output, "items", "array", [
56
+ "minItems",
57
+ "maxItems",
58
+ ]);
59
+ return parse_range("items", identifier, text, true);
60
+ },
61
+ minItems: (identifier, metadata, text, output) => {
62
+ validate(identifier, metadata, output, "minItems", "array", ["items"]);
63
+ return {
64
+ kind: "minItems",
65
+ value: parse_number(identifier, text),
66
+ };
67
+ },
68
+ maxItems: (identifier, metadata, text, output) => {
69
+ validate(identifier, metadata, output, "maxItems", "array", ["items"]);
70
+ return {
71
+ kind: "maxItems",
72
+ value: parse_number(identifier, text),
73
+ };
74
+ },
75
+
76
+ /* -----------------------------------------------------------
77
+ NUMBER
78
+ ----------------------------------------------------------- */
79
+ type: (identifier, metadata, text, output) => {
80
+ validate(identifier, metadata, output, "type", "number", []);
81
+ if (text !== "int" && text !== "uint")
82
+ throw new Error(`${LABEL}: invalid type tag on "${identifier()}".`);
83
+ return { kind: "type", value: text };
84
+ },
85
+ range: (identifier, metadata, text, output) => {
86
+ validate(identifier, metadata, output, "range", "number", [
87
+ "exclusiveMinimum",
88
+ "minimum",
89
+ "maximum",
90
+ "exclusiveMaximum",
91
+ ]);
92
+ return parse_range("range", identifier, text, false);
93
+ },
94
+ minimum: (identifier, metadata, text, output) => {
95
+ validate(identifier, metadata, output, "minimum", "number", [
96
+ "range",
97
+ "exclusiveMaximum",
98
+ "exclusiveMinimum",
99
+ ]);
100
+ return {
101
+ kind: "minimum",
102
+ value: parse_number(identifier, text),
103
+ };
104
+ },
105
+ maximum: (identifier, metadata, text, output) => {
106
+ validate(identifier, metadata, output, "maximum", "number", [
107
+ "range",
108
+ "exclusiveMinimum",
109
+ "exclusiveMaximum",
110
+ ]);
111
+ return {
112
+ kind: "maximum",
113
+ value: parse_number(identifier, text),
114
+ };
115
+ },
116
+ exclusiveMinimum: (identifier, metadata, text, output) => {
117
+ validate(identifier, metadata, output, "exclusiveMinimum", "number", [
118
+ "range",
119
+ "minimum",
120
+ "maximum",
121
+ ]);
122
+ return {
123
+ kind: "exclusiveMinimum",
124
+ value: parse_number(identifier, text),
125
+ };
126
+ },
127
+ exclusiveMaximum: (identifier, metadata, text, output) => {
128
+ validate(identifier, metadata, output, "exclusiveMaximum", "number", [
129
+ "range",
130
+ "minimum",
131
+ "maximum",
132
+ ]);
133
+ return {
134
+ kind: "exclusiveMaximum",
135
+ value: parse_number(identifier, text),
136
+ };
137
+ },
138
+ multipleOf: (identifier, metadata, text, output) => {
139
+ validate(identifier, metadata, output, "multipleOf", "number", [
140
+ "step",
141
+ ]);
142
+ return {
143
+ kind: "multipleOf",
144
+ value: parse_number(identifier, text),
145
+ };
146
+ },
147
+ step: (identifier, metadata, text, output) => {
148
+ validate(identifier, metadata, output, "step", "number", [
149
+ "multipleOf",
150
+ ]);
151
+
152
+ const minimum: boolean = output.some(
153
+ (tag) =>
154
+ tag.kind === "minimum" ||
155
+ tag.kind === "exclusiveMinimum" ||
156
+ (tag.kind === "range" && tag.minimum !== undefined),
157
+ );
158
+ if (minimum === undefined)
159
+ throw new Error(
160
+ `${LABEL}: step requires minimum tag on "${identifier()}".`,
161
+ );
162
+
163
+ return {
164
+ kind: "step",
165
+ value: parse_number(identifier, text),
166
+ };
167
+ },
168
+
169
+ /* -----------------------------------------------------------
170
+ STRING
171
+ ----------------------------------------------------------- */
172
+ format: (identifier, metadata, value, output) => {
173
+ validate(identifier, metadata, output, "format", "string", ["pattern"]);
174
+
175
+ // Ignore arbitrary @format values in the internal metadata,
176
+ // these are currently only supported on the typia.application() API.
177
+ if (FORMATS.has(value) === false) return null;
178
+
179
+ return {
180
+ kind: "format",
181
+ value: value as "uuid",
182
+ };
183
+ },
184
+ pattern: (identifier, metadata, value, output) => {
185
+ validate(identifier, metadata, output, "pattern", "string", ["format"]);
186
+ return {
187
+ kind: "pattern",
188
+ value,
189
+ };
190
+ },
191
+ length: (identifier, metadata, text, output) => {
192
+ validate(identifier, metadata, output, "length", "string", [
193
+ "minLength",
194
+ "maxLength",
195
+ ]);
196
+ return parse_range("length", identifier, text, true);
197
+ },
198
+ minLength: (identifier, metadata, text, output) => {
199
+ validate(identifier, metadata, output, "minLength", "string", [
200
+ "length",
201
+ ]);
202
+ return {
203
+ kind: "minLength",
204
+ value: parse_number(identifier, text),
205
+ };
206
+ },
207
+ maxLength: (identifier, metadata, text, output) => {
208
+ validate(identifier, metadata, output, "maxLength", "string", [
209
+ "length",
210
+ ]);
211
+ return {
212
+ kind: "maxLength",
213
+ value: parse_number(identifier, text),
214
+ };
215
+ },
216
+ };
217
+
218
+ function parse_range<Kind extends string>(
219
+ kind: Kind,
220
+ identifier: () => string,
221
+ text: string,
222
+ allowScalar: boolean,
223
+ ): Omit<IMetadataTag.IRange, "kind"> & { kind: Kind } {
224
+ if (allowScalar === true && Number.isNaN(Number(text)) === false) {
225
+ const value: number = Number(text);
226
+ if (Math.floor(value) !== value)
227
+ throw new Error(`${LABEL}: invalid length on "${identifier()}".`);
228
+ return {
229
+ kind,
230
+ minimum: {
231
+ include: true,
232
+ value,
233
+ },
234
+ maximum: {
235
+ include: true,
236
+ value,
237
+ },
238
+ };
239
+ } else if (text.indexOf(",") === -1)
240
+ if (LEFT_PARENTHESIS.some((str) => text.indexOf(str) !== -1))
241
+ return {
242
+ kind,
243
+ minimum: parse_side("left")(kind)(identifier)(text),
244
+ };
245
+ else if (RIGHT_PARENTHESIS.some((str) => text.indexOf(str) !== -1))
246
+ return {
247
+ kind,
248
+ maximum: parse_side("right")(kind)(identifier)(text),
249
+ };
250
+ else
251
+ throw new Error(
252
+ `${LABEL}: invalid ${kind} tag on "${identifier()}".`,
253
+ );
254
+
255
+ const [left, right] = text.split(",") as [string, string];
256
+ return {
257
+ kind,
258
+ minimum: parse_side("left")(kind)(identifier)(left),
259
+ maximum: parse_side("right")(kind)(identifier)(right),
260
+ };
261
+ }
262
+
263
+ const parse_side = (side: "left" | "right") => {
264
+ const symbol = side === "left" ? LEFT_PARENTHESIS : RIGHT_PARENTHESIS;
265
+ const substring: (str: string, index: number) => string =
266
+ side === "left"
267
+ ? (str, index) => str.substring(index + 1)
268
+ : (str, index) => str.substring(0, index);
269
+ return (tag: string) => (identifier: () => string) => (text: string) => {
270
+ const [index, include] =
271
+ text.indexOf(symbol[0]) !== -1
272
+ ? [text.indexOf(symbol[0]), true]
273
+ : [text.indexOf(symbol[1]), false];
274
+ if (index === -1)
275
+ throw new Error(
276
+ `${LABEL}: invalid ${tag} tag on "${identifier()}".`,
277
+ );
278
+ return {
279
+ include,
280
+ value: parse_number(identifier, substring(text, index)),
281
+ };
282
+ };
283
+ };
284
+
285
+ function parse_number(identifier: () => string, str: string): number {
286
+ const value: number = Number(str);
287
+ if (isNaN(value) === true)
288
+ throw new Error(`${LABEL}: invalid number on "${identifier()}".`);
289
+ return value;
290
+ }
291
+
292
+ const LABEL = "Error on typia.MetadataTagFactory.generate()";
293
+ const LEFT_PARENTHESIS = ["[", "("] as const;
294
+ const RIGHT_PARENTHESIS = ["]", ")"] as const;
295
+ const FORMATS = new Set(["uuid", "email", "url", "mobile", "ipv4", "ipv6"]);
296
+
297
+ const WRONG_TYPE = (
298
+ tag: string,
299
+ type: "string" | "number" | "array",
300
+ identifier: () => string,
301
+ ) => `${LABEL}: ${tag} requires ${type} type, but no "${identifier()}".`;
302
+
303
+ function validate(
304
+ identifier: () => string,
305
+ metadata: Metadata,
306
+ output: IMetadataTag[],
307
+ kind: IMetadataTag["kind"],
308
+ type: "array" | "string" | "number",
309
+ neighbors: IMetadataTag["kind"][],
310
+ ): void {
311
+ // TYPE CHECKING
312
+ if (type === "array") {
313
+ if (has_array(metadata) === false)
314
+ throw new Error(WRONG_TYPE(kind, "array", identifier));
315
+ } else if (has_atomic(metadata, type) === false)
316
+ throw new Error(WRONG_TYPE(kind, type, identifier));
317
+
318
+ // DUPLICATED TAG
319
+ if (output.some((tag) => tag.kind === kind))
320
+ throw new Error(
321
+ `${LABEL}: duplicated ${kind} tags on "${identifier()}".`,
322
+ );
323
+
324
+ // NEIGHBOR TAG
325
+ for (const name of neighbors)
326
+ if (output.some((tag) => tag.kind === name))
327
+ throw new Error(
328
+ `${LABEL}: ${kind} and ${name} tags on "${identifier()}".`,
329
+ );
330
+ }
331
+
332
+ function has_atomic(metadata: Metadata, type: "string" | "number"): boolean {
333
+ return (
334
+ metadata.atomics.find((atom) => atom === type) !== undefined ||
335
+ metadata.arrays.some((child) => has_atomic(child, type)) ||
336
+ metadata.tuples.some((tuple) =>
337
+ tuple.some((child) => has_atomic(child, type)),
338
+ )
339
+ );
340
+ }
341
+
342
+ function has_array(metadata: Metadata): boolean {
343
+ return (
344
+ metadata.arrays.length !== 0 ||
345
+ metadata.tuples.some((tuple) => tuple.some((child) => has_array(child)))
346
+ );
347
+ }