typia 3.4.15 → 3.5.0-dev.20221222

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 (169) hide show
  1. package/README.md +48 -7
  2. package/lib/factories/MetadataCollection.d.ts +1 -9
  3. package/lib/factories/MetadataCollection.js +2 -75
  4. package/lib/factories/MetadataCollection.js.map +1 -1
  5. package/lib/factories/ProtocolFactory.d.ts +8 -0
  6. package/lib/factories/ProtocolFactory.js +119 -0
  7. package/lib/factories/ProtocolFactory.js.map +1 -0
  8. package/lib/factories/internal/protocols/ProtocolMetadataUtil.d.ts +11 -0
  9. package/lib/factories/internal/protocols/ProtocolMetadataUtil.js +76 -0
  10. package/lib/factories/internal/protocols/ProtocolMetadataUtil.js.map +1 -0
  11. package/lib/factories/internal/protocols/emplace_protocol_object.d.ts +3 -0
  12. package/lib/factories/internal/protocols/emplace_protocol_object.js +47 -0
  13. package/lib/factories/internal/protocols/emplace_protocol_object.js.map +1 -0
  14. package/lib/factories/internal/protocols/emplace_protocol_property.d.ts +4 -0
  15. package/lib/factories/internal/protocols/emplace_protocol_property.js +20 -0
  16. package/lib/factories/internal/protocols/emplace_protocol_property.js.map +1 -0
  17. package/lib/factories/internal/protocols/iterate_protocol_atomic.d.ts +3 -0
  18. package/lib/factories/internal/protocols/iterate_protocol_atomic.js +69 -0
  19. package/lib/factories/internal/protocols/iterate_protocol_atomic.js.map +1 -0
  20. package/lib/factories/internal/protocols/iterate_protocol_constant.d.ts +2 -0
  21. package/lib/factories/internal/protocols/iterate_protocol_constant.js +30 -0
  22. package/lib/factories/internal/protocols/iterate_protocol_constant.js.map +1 -0
  23. package/lib/factories/internal/protocols/iterate_protocol_main.d.ts +3 -0
  24. package/lib/factories/internal/protocols/iterate_protocol_main.js +17 -0
  25. package/lib/factories/internal/protocols/iterate_protocol_main.js.map +1 -0
  26. package/lib/factories/internal/protocols/iterate_protocol_map.d.ts +3 -0
  27. package/lib/factories/internal/protocols/iterate_protocol_map.js +71 -0
  28. package/lib/factories/internal/protocols/iterate_protocol_map.js.map +1 -0
  29. package/lib/factories/internal/protocols/iterate_protocol_metadata.d.ts +5 -0
  30. package/lib/factories/internal/protocols/iterate_protocol_metadata.js +192 -0
  31. package/lib/factories/internal/protocols/iterate_protocol_metadata.js.map +1 -0
  32. package/lib/factories/internal/protocols/iterate_protocol_native.d.ts +2 -0
  33. package/lib/factories/internal/protocols/iterate_protocol_native.js +33 -0
  34. package/lib/factories/internal/protocols/iterate_protocol_native.js.map +1 -0
  35. package/lib/factories/internal/protocols/iterate_protocol_repeated.d.ts +4 -0
  36. package/lib/factories/internal/protocols/iterate_protocol_repeated.js +24 -0
  37. package/lib/factories/internal/protocols/iterate_protocol_repeated.js.map +1 -0
  38. package/lib/factories/internal/protocols/iterate_protocol_tuple.d.ts +3 -0
  39. package/lib/factories/internal/protocols/iterate_protocol_tuple.js +46 -0
  40. package/lib/factories/internal/protocols/iterate_protocol_tuple.js.map +1 -0
  41. package/lib/functional/$proto_bytes.d.ts +2 -0
  42. package/lib/functional/$proto_bytes.js +37 -0
  43. package/lib/functional/$proto_bytes.js.map +1 -0
  44. package/lib/functional/$proto_field.d.ts +10 -0
  45. package/lib/functional/$proto_field.js +42 -0
  46. package/lib/functional/$proto_field.js.map +1 -0
  47. package/lib/functional/$proto_float.d.ts +4 -0
  48. package/lib/functional/$proto_float.js +28 -0
  49. package/lib/functional/$proto_float.js.map +1 -0
  50. package/lib/functional/$proto_i32.d.ts +2 -0
  51. package/lib/functional/$proto_i32.js +23 -0
  52. package/lib/functional/$proto_i32.js.map +1 -0
  53. package/lib/functional/$proto_i64.d.ts +2 -0
  54. package/lib/functional/$proto_i64.js +31 -0
  55. package/lib/functional/$proto_i64.js.map +1 -0
  56. package/lib/functional/$proto_size.d.ts +6 -0
  57. package/lib/functional/$proto_size.js +76 -0
  58. package/lib/functional/$proto_size.js.map +1 -0
  59. package/lib/functional/$proto_string.d.ts +2 -0
  60. package/lib/functional/$proto_string.js +34 -0
  61. package/lib/functional/$proto_string.js.map +1 -0
  62. package/lib/functional/$varint.d.ts +6 -0
  63. package/lib/functional/$varint.js +99 -0
  64. package/lib/functional/$varint.js.map +1 -0
  65. package/lib/functional/$zigzag.d.ts +4 -0
  66. package/lib/functional/$zigzag.js +34 -0
  67. package/lib/functional/$zigzag.js.map +1 -0
  68. package/lib/messages/IProtocolMessage.d.ts +5 -0
  69. package/lib/messages/IProtocolMessage.js +3 -0
  70. package/lib/messages/IProtocolMessage.js.map +1 -0
  71. package/lib/messages/IProtocolProperty.d.ts +11 -0
  72. package/lib/messages/IProtocolProperty.js +3 -0
  73. package/lib/messages/IProtocolProperty.js.map +1 -0
  74. package/lib/metadata/IMetadataTag.d.ts +7 -3
  75. package/lib/metadata/Metadata.js +1 -1
  76. package/lib/metadata/Metadata.js.map +1 -1
  77. package/lib/module.d.ts +2 -0
  78. package/lib/module.js +5 -1
  79. package/lib/module.js.map +1 -1
  80. package/lib/programmers/MessageProgrammer.d.ts +5 -0
  81. package/lib/programmers/MessageProgrammer.js +134 -0
  82. package/lib/programmers/MessageProgrammer.js.map +1 -0
  83. package/lib/programmers/internal/application_object.js +9 -9
  84. package/lib/programmers/internal/application_object.js.map +1 -1
  85. package/lib/programmers/internal/application_schema.js +5 -3
  86. package/lib/programmers/internal/application_schema.js.map +1 -1
  87. package/lib/transformers/CallExpressionTransformer.js +3 -1
  88. package/lib/transformers/CallExpressionTransformer.js.map +1 -1
  89. package/lib/transformers/features/miscellaneous/ApplicationTransformer.js +1 -3
  90. package/lib/transformers/features/miscellaneous/ApplicationTransformer.js.map +1 -1
  91. package/lib/transformers/features/protocols/MessageTransformer.d.ts +5 -0
  92. package/lib/transformers/features/protocols/MessageTransformer.js +17 -0
  93. package/lib/transformers/features/protocols/MessageTransformer.js.map +1 -0
  94. package/lib/utils/NameEncoder.d.ts +4 -0
  95. package/lib/utils/NameEncoder.js +89 -0
  96. package/lib/utils/NameEncoder.js.map +1 -0
  97. package/package.json +2 -3
  98. package/src/executable/internal/TypiaSetupWizard.ts +173 -173
  99. package/src/factories/MetadataCollection.ts +83 -122
  100. package/src/factories/MetadataFactory.ts +47 -47
  101. package/src/factories/MetadataTagFactory.ts +351 -351
  102. package/src/factories/ProtocolFactory.ts +92 -0
  103. package/src/factories/internal/metadata/MetadataHelper.ts +12 -12
  104. package/src/factories/internal/metadata/emplace_metadata_object.ts +140 -140
  105. package/src/factories/internal/metadata/explore_metadata.ts +92 -92
  106. package/src/factories/internal/metadata/iterate_metadata.ts +80 -80
  107. package/src/factories/internal/metadata/iterate_metadata_array.ts +29 -29
  108. package/src/factories/internal/metadata/iterate_metadata_atomic.ts +59 -59
  109. package/src/factories/internal/metadata/iterate_metadata_coalesce.ts +33 -33
  110. package/src/factories/internal/metadata/iterate_metadata_constant.ts +58 -58
  111. package/src/factories/internal/metadata/iterate_metadata_map.ts +41 -41
  112. package/src/factories/internal/metadata/iterate_metadata_native.ts +227 -227
  113. package/src/factories/internal/metadata/iterate_metadata_object.ts +48 -48
  114. package/src/factories/internal/metadata/iterate_metadata_resolve.ts +27 -27
  115. package/src/factories/internal/metadata/iterate_metadata_set.ts +33 -33
  116. package/src/factories/internal/metadata/iterate_metadata_template.ts +38 -38
  117. package/src/factories/internal/metadata/iterate_metadata_tuple.ts +45 -45
  118. package/src/factories/internal/metadata/iterate_metadata_union.ts +59 -59
  119. package/src/factories/internal/protocols/ProtocolMetadataUtil.ts +81 -0
  120. package/src/factories/internal/protocols/emplace_protocol_object.ts +25 -0
  121. package/src/factories/internal/protocols/emplace_protocol_property.ts +12 -0
  122. package/src/factories/internal/protocols/iterate_protocol_atomic.ts +34 -0
  123. package/src/factories/internal/protocols/iterate_protocol_constant.ts +27 -0
  124. package/src/factories/internal/protocols/iterate_protocol_main.ts +19 -0
  125. package/src/factories/internal/protocols/iterate_protocol_map.ts +38 -0
  126. package/src/factories/internal/protocols/iterate_protocol_metadata.ts +76 -0
  127. package/src/factories/internal/protocols/iterate_protocol_native.ts +34 -0
  128. package/src/factories/internal/protocols/iterate_protocol_repeated.ts +25 -0
  129. package/src/factories/internal/protocols/iterate_protocol_tuple.ts +25 -0
  130. package/src/functional/$number.ts +12 -12
  131. package/src/functional/$proto_bytes.ts +25 -0
  132. package/src/functional/$proto_field.ts +30 -0
  133. package/src/functional/$proto_float.ts +37 -0
  134. package/src/functional/$proto_i32.ts +29 -0
  135. package/src/functional/$proto_i64.ts +37 -0
  136. package/src/functional/$proto_size.ts +82 -0
  137. package/src/functional/$proto_string.ts +24 -0
  138. package/src/functional/$varint.ts +130 -0
  139. package/src/functional/$zigzag.ts +39 -0
  140. package/src/messages/IProtocolMessage.ts +6 -0
  141. package/src/messages/IProtocolProperty.ts +11 -0
  142. package/src/metadata/IMetadataTag.ts +137 -122
  143. package/src/metadata/Metadata.ts +479 -477
  144. package/src/module.ts +1547 -1535
  145. package/src/programmers/AssertParseProgrammer.ts +55 -55
  146. package/src/programmers/AssertProgrammer.ts +445 -445
  147. package/src/programmers/AssertStringifyProgrammer.ts +65 -65
  148. package/src/programmers/CheckerProgrammer.ts +810 -810
  149. package/src/programmers/IsParseProgrammer.ts +61 -61
  150. package/src/programmers/IsProgrammer.ts +175 -175
  151. package/src/programmers/IsStringifyProgrammer.ts +69 -69
  152. package/src/programmers/MessageProgrammer.ts +116 -0
  153. package/src/programmers/StringifyProgrammer.ts +762 -762
  154. package/src/programmers/ValidateParseProgrammer.ts +59 -59
  155. package/src/programmers/ValidateProgrammer.ts +237 -237
  156. package/src/programmers/ValidateStringifyProgrammer.ts +80 -80
  157. package/src/programmers/helpers/OptionPredicator.ts +15 -15
  158. package/src/programmers/internal/application_default.ts +17 -17
  159. package/src/programmers/internal/application_object.ts +5 -5
  160. package/src/programmers/internal/application_schema.ts +231 -228
  161. package/src/programmers/internal/check_bigint.ts +85 -85
  162. package/src/programmers/internal/check_number.ts +175 -175
  163. package/src/schemas/IJsonSchema.ts +90 -90
  164. package/src/transformers/CallExpressionTransformer.ts +131 -124
  165. package/src/transformers/ITransformOptions.ts +47 -47
  166. package/src/transformers/features/miscellaneous/ApplicationTransformer.ts +117 -119
  167. package/src/transformers/features/protocols/MessageTransformer.ts +32 -0
  168. package/src/transformers/features/stringifiers/StringifyTransformer.ts +46 -46
  169. package/src/utils/NameEncoder.ts +32 -0
@@ -1,351 +1,351 @@
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
- const valid =
334
- type === "number"
335
- ? (atom: string) => atom === type || atom === "bigint"
336
- : (atom: string) => atom === type;
337
- return (
338
- metadata.atomics.find((atom) => valid(atom)) !== undefined ||
339
- metadata.arrays.some((child) => has_atomic(child, type)) ||
340
- metadata.tuples.some((tuple) =>
341
- tuple.some((child) => has_atomic(child, type)),
342
- )
343
- );
344
- }
345
-
346
- function has_array(metadata: Metadata): boolean {
347
- return (
348
- metadata.arrays.length !== 0 ||
349
- metadata.tuples.some((tuple) => tuple.some((child) => has_array(child)))
350
- );
351
- }
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
+ const valid =
334
+ type === "number"
335
+ ? (atom: string) => atom === type || atom === "bigint"
336
+ : (atom: string) => atom === type;
337
+ return (
338
+ metadata.atomics.find((atom) => valid(atom)) !== undefined ||
339
+ metadata.arrays.some((child) => has_atomic(child, type)) ||
340
+ metadata.tuples.some((tuple) =>
341
+ tuple.some((child) => has_atomic(child, type)),
342
+ )
343
+ );
344
+ }
345
+
346
+ function has_array(metadata: Metadata): boolean {
347
+ return (
348
+ metadata.arrays.length !== 0 ||
349
+ metadata.tuples.some((tuple) => tuple.some((child) => has_array(child)))
350
+ );
351
+ }