typia 5.1.3 → 5.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/executable/TypiaSetupWizard.js +1 -19
- package/lib/executable/TypiaSetupWizard.js.map +1 -1
- package/package.json +1 -1
- package/src/executable/TypiaSetupWizard.ts +1 -16
- package/src/factories/MetadataCollection.ts +277 -277
- package/src/factories/MetadataFactory.ts +238 -238
- package/src/factories/MetadataTypeTagFactory.ts +325 -325
- package/src/factories/internal/metadata/emend_metadata_atomics.ts +41 -41
- package/src/factories/internal/metadata/iterate_metadata_intersection.ts +259 -259
- package/src/functional/$HeadersReader.ts +28 -28
- package/src/functional/$ParameterReader.ts +31 -31
- package/src/functional/$QueryReader.ts +56 -56
- package/src/functional/Namespace.ts +142 -142
- package/src/http.ts +1149 -1149
- package/src/json.ts +648 -648
- package/src/misc.ts +651 -651
- package/src/module.ts +657 -657
- package/src/programmers/helpers/HttpMetadataUtil.ts +21 -21
- package/src/programmers/http/HttpAssertHeadersProgrammer.ts +77 -77
- package/src/programmers/http/HttpAssertQueryProgrammer.ts +77 -77
- package/src/programmers/http/HttpHeadersProgrammer.ts +339 -339
- package/src/programmers/http/HttpIsHeadersProgrammer.ts +87 -87
- package/src/programmers/http/HttpIsQueryProgrammer.ts +87 -87
- package/src/programmers/http/HttpParameterProgrammer.ts +104 -104
- package/src/programmers/http/HttpQueryProgrammer.ts +273 -273
- package/src/programmers/http/HttpValidateHeadersProgrammer.ts +77 -77
- package/src/programmers/http/HttpValidateQueryProgrammer.ts +77 -77
- package/src/programmers/internal/application_boolean.ts +30 -30
- package/src/programmers/internal/application_number.ts +90 -90
- package/src/programmers/internal/application_schema.ts +180 -180
- package/src/programmers/internal/application_string.ts +54 -54
- package/src/programmers/internal/check_array_length.ts +44 -44
- package/src/programmers/internal/check_bigint.ts +48 -48
- package/src/programmers/internal/check_number.ts +108 -108
- package/src/programmers/internal/check_string.ts +48 -48
- package/src/programmers/protobuf/ProtobufEncodeProgrammer.ts +882 -882
- package/src/protobuf.ts +887 -887
- package/src/schemas/json/IJsonComponents.ts +34 -34
- package/src/schemas/json/IJsonSchema.ts +112 -112
- package/src/schemas/metadata/IMetadataConstant.ts +25 -25
- package/src/schemas/metadata/IMetadataTypeTag.ts +8 -8
- package/src/schemas/metadata/Metadata.ts +686 -686
- package/src/tags/Default.ts +15 -15
- package/src/tags/Format.ts +30 -30
- package/src/tags/Pattern.ts +9 -9
- package/src/tags/TagBase.ts +68 -68
- package/src/tags/index.ts +14 -14
- package/src/transformers/CallExpressionTransformer.ts +289 -289
- package/src/transformers/features/http/CreateHttpAssertHeadersTransformer.ts +12 -12
- package/src/transformers/features/http/CreateHttpAssertQueryTransformer.ts +12 -12
- package/src/transformers/features/http/CreateHttpHeadersTransformer.ts +9 -9
- package/src/transformers/features/http/CreateHttpIsHeadersTransformer.ts +9 -9
- package/src/transformers/features/http/CreateHttpIsQueryTransformer.ts +9 -9
- package/src/transformers/features/http/CreateHttpParameterTransformer.ts +9 -9
- package/src/transformers/features/http/CreateHttpQueryTransformer.ts +9 -9
- package/src/transformers/features/http/CreateHttpValidateHeadersTransformer.ts +12 -12
- package/src/transformers/features/http/CreateHttpValidateQueryTransformer.ts +12 -12
- package/src/transformers/features/http/HttpAssertHeadersTransformer.ts +10 -10
- package/src/transformers/features/http/HttpAssertQueryTransformer.ts +10 -10
- package/src/transformers/features/http/HttpHeadersTransformer.ts +9 -9
- package/src/transformers/features/http/HttpIsHeadersTransformer.ts +9 -9
- package/src/transformers/features/http/HttpIsQueryTransformer.ts +9 -9
- package/src/transformers/features/http/HttpParameterTransformer.ts +9 -9
- package/src/transformers/features/http/HttpQueryTransformer.ts +9 -9
- package/src/transformers/features/http/HttpValidateHeadersTransformer.ts +10 -10
- package/src/transformers/features/http/HttpValidateQueryTransformer.ts +10 -10
|
@@ -1,325 +1,325 @@
|
|
|
1
|
-
import { IMetadataTypeTag } from "../schemas/metadata/IMetadataTypeTag";
|
|
2
|
-
import { Metadata } from "../schemas/metadata/Metadata";
|
|
3
|
-
import { MetadataObject } from "../schemas/metadata/MetadataObject";
|
|
4
|
-
import { MetadataProperty } from "../schemas/metadata/MetadataProperty";
|
|
5
|
-
|
|
6
|
-
import { MetadataFactory } from "./MetadataFactory";
|
|
7
|
-
|
|
8
|
-
export namespace MetadataTypeTagFactory {
|
|
9
|
-
export const analyze =
|
|
10
|
-
(errors: MetadataFactory.IError[]) =>
|
|
11
|
-
(type: "boolean" | "bigint" | "number" | "string" | "array") =>
|
|
12
|
-
(
|
|
13
|
-
objects: MetadataObject[],
|
|
14
|
-
explore: MetadataFactory.IExplore,
|
|
15
|
-
): IMetadataTypeTag[] => {
|
|
16
|
-
const messages: string[] = [];
|
|
17
|
-
const report =
|
|
18
|
-
(property: string | null) =>
|
|
19
|
-
(msg: string): false => {
|
|
20
|
-
messages.push(
|
|
21
|
-
`the property ${
|
|
22
|
-
property === null
|
|
23
|
-
? `["typia.tag"]`
|
|
24
|
-
: `["typia.tag.${property}"]`
|
|
25
|
-
} ${msg}.`,
|
|
26
|
-
);
|
|
27
|
-
return false;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
//----
|
|
31
|
-
// VALIDATION PROCESS
|
|
32
|
-
//----
|
|
33
|
-
const filtered: MetadataObject[] = objects.filter((obj) => {
|
|
34
|
-
// ONLY ONE PROPERTY
|
|
35
|
-
if (obj.properties.length !== 1) return false;
|
|
36
|
-
|
|
37
|
-
// THE TAG.TYPE PROPERTY MUST BE
|
|
38
|
-
const top: MetadataProperty = obj.properties[0]!;
|
|
39
|
-
if (
|
|
40
|
-
top.key.getSoleLiteral() !== "typia.tag" ||
|
|
41
|
-
top.value.size() !== 1 ||
|
|
42
|
-
top.value.objects.length !== 1
|
|
43
|
-
)
|
|
44
|
-
return false;
|
|
45
|
-
else if (top.value.optional === false)
|
|
46
|
-
return report(null)("must be optional object");
|
|
47
|
-
|
|
48
|
-
// CHECK LIST OF PROPERTIES
|
|
49
|
-
const tag: MetadataObject = top.value.objects[0]!;
|
|
50
|
-
const statics: string[] = tag.properties
|
|
51
|
-
.map((p) => p.key.getSoleLiteral()!)
|
|
52
|
-
.filter((str) => str !== null);
|
|
53
|
-
if (ESSENTIAL_FIELDS.some((f) => !statics.includes(f)))
|
|
54
|
-
return report(null)(
|
|
55
|
-
`must have at least three properties - ${ESSENTIAL_FIELDS.map(
|
|
56
|
-
(str) => `'${str}'`,
|
|
57
|
-
).join(", ")}`,
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
const each: boolean[] = tag.properties.map((p) => {
|
|
61
|
-
const key: string | null = p.key.getSoleLiteral();
|
|
62
|
-
if (key === null) return true;
|
|
63
|
-
else if (FIELDS.includes(key) === false) return true;
|
|
64
|
-
return validate_property(report)(key, p.value);
|
|
65
|
-
});
|
|
66
|
-
return each.every((v) => v === true);
|
|
67
|
-
});
|
|
68
|
-
if (filtered.length === 0) return [];
|
|
69
|
-
|
|
70
|
-
//----
|
|
71
|
-
// CONSTRUCT TYPE TAGS
|
|
72
|
-
//----
|
|
73
|
-
// CREATE 1ST
|
|
74
|
-
const tagList: Array<ITypeTag | null> = filtered.map(
|
|
75
|
-
create_metadata_type_tag(report),
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
const output: IMetadataTypeTag[] = [];
|
|
79
|
-
for (const tag of tagList)
|
|
80
|
-
if (tag !== null)
|
|
81
|
-
output.push({
|
|
82
|
-
target: tag.target.some((str) => str === type)
|
|
83
|
-
? type
|
|
84
|
-
: null!,
|
|
85
|
-
name: tag.name,
|
|
86
|
-
kind: tag.kind,
|
|
87
|
-
value: tag.value,
|
|
88
|
-
validate: tag.validate[type]!,
|
|
89
|
-
exclusive: tag.exclusive,
|
|
90
|
-
});
|
|
91
|
-
validate(report)(type)(output);
|
|
92
|
-
|
|
93
|
-
if (messages.length > 0) {
|
|
94
|
-
errors.push({
|
|
95
|
-
name: [type, ...objects.map((o) => o.name)].join(" & "),
|
|
96
|
-
explore,
|
|
97
|
-
messages,
|
|
98
|
-
});
|
|
99
|
-
return [];
|
|
100
|
-
}
|
|
101
|
-
return output;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export const validate =
|
|
105
|
-
(report: (property: string | null) => (msg: string) => false) =>
|
|
106
|
-
(type: "boolean" | "bigint" | "number" | "string" | "array") =>
|
|
107
|
-
(tagList: IMetadataTypeTag[]): boolean => {
|
|
108
|
-
let success: boolean = true;
|
|
109
|
-
for (const tag of tagList)
|
|
110
|
-
if (tag.target !== type) {
|
|
111
|
-
success &&= report(null)(
|
|
112
|
-
`target must constains ${type} type`,
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
tagList.forEach((tag, i) => {
|
|
117
|
-
if (tag.exclusive === false) return;
|
|
118
|
-
else if (tag.exclusive === true) {
|
|
119
|
-
const some: boolean = tagList.some(
|
|
120
|
-
(opposite, j) => i !== j && opposite.kind === tag.kind,
|
|
121
|
-
);
|
|
122
|
-
if (some === true)
|
|
123
|
-
success &&= report(null)(
|
|
124
|
-
`kind '${tag.kind}' can't be duplicated`,
|
|
125
|
-
);
|
|
126
|
-
} else if (Array.isArray(tag.exclusive)) {
|
|
127
|
-
const some: IMetadataTypeTag | undefined = tagList.find(
|
|
128
|
-
(opposite, j) =>
|
|
129
|
-
i !== j &&
|
|
130
|
-
opposite.kind === tag.kind &&
|
|
131
|
-
(tag.exclusive as string[]).includes(opposite.name),
|
|
132
|
-
);
|
|
133
|
-
if (some !== undefined)
|
|
134
|
-
success ??= report(null)(
|
|
135
|
-
`kind '${tag.kind}' can't be used with '${some.name}'`,
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
return success;
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const validate_property =
|
|
143
|
-
(report: (property: string | null) => (msg: string) => false) =>
|
|
144
|
-
(key: string, value: Metadata): boolean => {
|
|
145
|
-
if (
|
|
146
|
-
// TARGET
|
|
147
|
-
key === "target" &&
|
|
148
|
-
(value.constants.length !== 1 ||
|
|
149
|
-
value.constants[0]!.values.length !== value.size() ||
|
|
150
|
-
value.constants[0]!.values.some(
|
|
151
|
-
(v) =>
|
|
152
|
-
v !== "boolean" &&
|
|
153
|
-
v !== "bigint" &&
|
|
154
|
-
v !== "number" &&
|
|
155
|
-
v !== "string" &&
|
|
156
|
-
v !== "array",
|
|
157
|
-
))
|
|
158
|
-
)
|
|
159
|
-
return report(key)(
|
|
160
|
-
`must be one of 'boolean', 'bigint', 'number', 'string', 'array'`,
|
|
161
|
-
);
|
|
162
|
-
else if (
|
|
163
|
-
// KIND
|
|
164
|
-
key === "kind" &&
|
|
165
|
-
(value.size() !== 1 ||
|
|
166
|
-
value.constants.length !== 1 ||
|
|
167
|
-
value.constants[0]!.type !== "string" ||
|
|
168
|
-
value.constants[0]!.values.length !== 1)
|
|
169
|
-
)
|
|
170
|
-
return report(key)("must be a string literal type");
|
|
171
|
-
else if (
|
|
172
|
-
// VALUE
|
|
173
|
-
key === "value" &&
|
|
174
|
-
(value.size() > 1 ||
|
|
175
|
-
(value.size() !== 0 &&
|
|
176
|
-
(value.constants.length !== 1 ||
|
|
177
|
-
value.constants[0]!.values.length !== 1)))
|
|
178
|
-
)
|
|
179
|
-
return report(key)(
|
|
180
|
-
"must be a constant literal type or undefined value",
|
|
181
|
-
);
|
|
182
|
-
else if (key === "exclusive")
|
|
183
|
-
return get_exclusive(report)(key)(value) !== null;
|
|
184
|
-
else if (key === "validate") {
|
|
185
|
-
//----
|
|
186
|
-
// VALIDATE
|
|
187
|
-
//----
|
|
188
|
-
// UNDEFINED CASE
|
|
189
|
-
if (
|
|
190
|
-
value.size() === 0 &&
|
|
191
|
-
value.isRequired() === false &&
|
|
192
|
-
value.nullable === false
|
|
193
|
-
)
|
|
194
|
-
return true;
|
|
195
|
-
|
|
196
|
-
// STRING CASE
|
|
197
|
-
if (
|
|
198
|
-
value.size() === 1 &&
|
|
199
|
-
value.constants.length === 1 &&
|
|
200
|
-
value.constants[0]!.type === "string" &&
|
|
201
|
-
(value.constants[0]!.values.length === 1) === true
|
|
202
|
-
)
|
|
203
|
-
return true;
|
|
204
|
-
|
|
205
|
-
// RECORD<TARGET, STRING>
|
|
206
|
-
const target: string[] | undefined =
|
|
207
|
-
value.objects[0]?.properties
|
|
208
|
-
.map((p) => p.key.getSoleLiteral()!)
|
|
209
|
-
.filter((str) => str !== null) as string[] | undefined;
|
|
210
|
-
if (target === undefined)
|
|
211
|
-
return report("target")(
|
|
212
|
-
`must be one of 'boolean', 'bigint', 'number', 'string', 'array'`,
|
|
213
|
-
);
|
|
214
|
-
const variadic: boolean =
|
|
215
|
-
value.size() === 1 &&
|
|
216
|
-
value.objects.length === 1 &&
|
|
217
|
-
value.objects[0]!.properties.every(
|
|
218
|
-
(vp) =>
|
|
219
|
-
vp.value.size() === 1 &&
|
|
220
|
-
vp.value.isRequired() &&
|
|
221
|
-
vp.value.nullable === false &&
|
|
222
|
-
vp.value.constants.length === 1 &&
|
|
223
|
-
vp.value.constants[0]!.type === "string" &&
|
|
224
|
-
vp.value.constants[0]!.values.length === 1 &&
|
|
225
|
-
target.includes(vp.key.getSoleLiteral()!),
|
|
226
|
-
);
|
|
227
|
-
if (variadic === false)
|
|
228
|
-
return report(key)(
|
|
229
|
-
`must be a string literal type or Record<Target, string> type.`,
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
return true;
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
const create_metadata_type_tag =
|
|
236
|
-
(report: (property: string | null) => (msg: string) => false) =>
|
|
237
|
-
(obj: MetadataObject): ITypeTag | null => {
|
|
238
|
-
const find = (key: string): MetadataProperty | undefined =>
|
|
239
|
-
obj.properties[0]?.value.objects[0]?.properties.find(
|
|
240
|
-
(p) => p.key.getSoleLiteral() === key,
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
const target = find("target")!.value.constants[0]!
|
|
244
|
-
.values as ITypeTag["target"];
|
|
245
|
-
const kind: string = find("kind")!.value.constants[0]!
|
|
246
|
-
.values[0] as string;
|
|
247
|
-
const value: boolean | bigint | number | string | undefined =
|
|
248
|
-
find("value")?.value.constants[0]?.values[0];
|
|
249
|
-
const exclusive: string[] | boolean | null = get_exclusive(report)(
|
|
250
|
-
"exclusive",
|
|
251
|
-
)(find("exclusive")?.value);
|
|
252
|
-
if (exclusive === null) return null;
|
|
253
|
-
|
|
254
|
-
const validate: Record<string, string> = (() => {
|
|
255
|
-
const validate = find("validate")?.value;
|
|
256
|
-
if (!validate || validate.size() === 0) return {};
|
|
257
|
-
else if (validate.constants.length)
|
|
258
|
-
return Object.fromEntries(
|
|
259
|
-
target.map((t) => [
|
|
260
|
-
t,
|
|
261
|
-
validate.constants[0]!.values[0] as string,
|
|
262
|
-
]),
|
|
263
|
-
);
|
|
264
|
-
return Object.fromEntries(
|
|
265
|
-
validate.objects[0]!.properties.map((p) => [
|
|
266
|
-
p.key.getSoleLiteral()!,
|
|
267
|
-
p.value.constants[0]!.values[0]! as string,
|
|
268
|
-
]),
|
|
269
|
-
);
|
|
270
|
-
})();
|
|
271
|
-
|
|
272
|
-
return {
|
|
273
|
-
name: obj.name,
|
|
274
|
-
target,
|
|
275
|
-
kind,
|
|
276
|
-
value,
|
|
277
|
-
validate,
|
|
278
|
-
exclusive: exclusive ?? false,
|
|
279
|
-
};
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
const get_exclusive =
|
|
283
|
-
(report: (property: string | null) => (msg: string) => false) =>
|
|
284
|
-
(key: string) =>
|
|
285
|
-
(value: Metadata | undefined): boolean | string[] | null => {
|
|
286
|
-
if (value === undefined) return false;
|
|
287
|
-
else if (
|
|
288
|
-
value.size() === 1 &&
|
|
289
|
-
value.constants.length === 1 &&
|
|
290
|
-
value.constants[0]!.type === "boolean" &&
|
|
291
|
-
value.constants[0]!.values.length === 1
|
|
292
|
-
)
|
|
293
|
-
return value.constants[0]!.values[0]! as boolean;
|
|
294
|
-
else if (
|
|
295
|
-
value.size() === 1 &&
|
|
296
|
-
value.tuples.length === 1 &&
|
|
297
|
-
value.tuples[0]!.type.elements.every(
|
|
298
|
-
(elem) =>
|
|
299
|
-
elem.size() === 1 &&
|
|
300
|
-
elem.constants.length === 1 &&
|
|
301
|
-
elem.constants[0]!.type === "string" &&
|
|
302
|
-
elem.constants[0]!.values.length === 1,
|
|
303
|
-
)
|
|
304
|
-
)
|
|
305
|
-
return value.tuples[0]!.type.elements.map(
|
|
306
|
-
(elem) => elem.constants[0]!.values[0]! as string,
|
|
307
|
-
);
|
|
308
|
-
report(key)(
|
|
309
|
-
"must a boolean literal type or a tuple of string literal types.",
|
|
310
|
-
);
|
|
311
|
-
return null;
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
interface ITypeTag {
|
|
316
|
-
name: string;
|
|
317
|
-
target: Array<"bigint" | "number" | "string" | "array">;
|
|
318
|
-
kind: string;
|
|
319
|
-
value?: boolean | bigint | number | string;
|
|
320
|
-
validate: Record<string, string>;
|
|
321
|
-
exclusive: boolean | string[];
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const ESSENTIAL_FIELDS = ["target", "kind", "value"];
|
|
325
|
-
const FIELDS = [...ESSENTIAL_FIELDS, "validate", "exclusive"];
|
|
1
|
+
import { IMetadataTypeTag } from "../schemas/metadata/IMetadataTypeTag";
|
|
2
|
+
import { Metadata } from "../schemas/metadata/Metadata";
|
|
3
|
+
import { MetadataObject } from "../schemas/metadata/MetadataObject";
|
|
4
|
+
import { MetadataProperty } from "../schemas/metadata/MetadataProperty";
|
|
5
|
+
|
|
6
|
+
import { MetadataFactory } from "./MetadataFactory";
|
|
7
|
+
|
|
8
|
+
export namespace MetadataTypeTagFactory {
|
|
9
|
+
export const analyze =
|
|
10
|
+
(errors: MetadataFactory.IError[]) =>
|
|
11
|
+
(type: "boolean" | "bigint" | "number" | "string" | "array") =>
|
|
12
|
+
(
|
|
13
|
+
objects: MetadataObject[],
|
|
14
|
+
explore: MetadataFactory.IExplore,
|
|
15
|
+
): IMetadataTypeTag[] => {
|
|
16
|
+
const messages: string[] = [];
|
|
17
|
+
const report =
|
|
18
|
+
(property: string | null) =>
|
|
19
|
+
(msg: string): false => {
|
|
20
|
+
messages.push(
|
|
21
|
+
`the property ${
|
|
22
|
+
property === null
|
|
23
|
+
? `["typia.tag"]`
|
|
24
|
+
: `["typia.tag.${property}"]`
|
|
25
|
+
} ${msg}.`,
|
|
26
|
+
);
|
|
27
|
+
return false;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
//----
|
|
31
|
+
// VALIDATION PROCESS
|
|
32
|
+
//----
|
|
33
|
+
const filtered: MetadataObject[] = objects.filter((obj) => {
|
|
34
|
+
// ONLY ONE PROPERTY
|
|
35
|
+
if (obj.properties.length !== 1) return false;
|
|
36
|
+
|
|
37
|
+
// THE TAG.TYPE PROPERTY MUST BE
|
|
38
|
+
const top: MetadataProperty = obj.properties[0]!;
|
|
39
|
+
if (
|
|
40
|
+
top.key.getSoleLiteral() !== "typia.tag" ||
|
|
41
|
+
top.value.size() !== 1 ||
|
|
42
|
+
top.value.objects.length !== 1
|
|
43
|
+
)
|
|
44
|
+
return false;
|
|
45
|
+
else if (top.value.optional === false)
|
|
46
|
+
return report(null)("must be optional object");
|
|
47
|
+
|
|
48
|
+
// CHECK LIST OF PROPERTIES
|
|
49
|
+
const tag: MetadataObject = top.value.objects[0]!;
|
|
50
|
+
const statics: string[] = tag.properties
|
|
51
|
+
.map((p) => p.key.getSoleLiteral()!)
|
|
52
|
+
.filter((str) => str !== null);
|
|
53
|
+
if (ESSENTIAL_FIELDS.some((f) => !statics.includes(f)))
|
|
54
|
+
return report(null)(
|
|
55
|
+
`must have at least three properties - ${ESSENTIAL_FIELDS.map(
|
|
56
|
+
(str) => `'${str}'`,
|
|
57
|
+
).join(", ")}`,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const each: boolean[] = tag.properties.map((p) => {
|
|
61
|
+
const key: string | null = p.key.getSoleLiteral();
|
|
62
|
+
if (key === null) return true;
|
|
63
|
+
else if (FIELDS.includes(key) === false) return true;
|
|
64
|
+
return validate_property(report)(key, p.value);
|
|
65
|
+
});
|
|
66
|
+
return each.every((v) => v === true);
|
|
67
|
+
});
|
|
68
|
+
if (filtered.length === 0) return [];
|
|
69
|
+
|
|
70
|
+
//----
|
|
71
|
+
// CONSTRUCT TYPE TAGS
|
|
72
|
+
//----
|
|
73
|
+
// CREATE 1ST
|
|
74
|
+
const tagList: Array<ITypeTag | null> = filtered.map(
|
|
75
|
+
create_metadata_type_tag(report),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const output: IMetadataTypeTag[] = [];
|
|
79
|
+
for (const tag of tagList)
|
|
80
|
+
if (tag !== null)
|
|
81
|
+
output.push({
|
|
82
|
+
target: tag.target.some((str) => str === type)
|
|
83
|
+
? type
|
|
84
|
+
: null!,
|
|
85
|
+
name: tag.name,
|
|
86
|
+
kind: tag.kind,
|
|
87
|
+
value: tag.value,
|
|
88
|
+
validate: tag.validate[type]!,
|
|
89
|
+
exclusive: tag.exclusive,
|
|
90
|
+
});
|
|
91
|
+
validate(report)(type)(output);
|
|
92
|
+
|
|
93
|
+
if (messages.length > 0) {
|
|
94
|
+
errors.push({
|
|
95
|
+
name: [type, ...objects.map((o) => o.name)].join(" & "),
|
|
96
|
+
explore,
|
|
97
|
+
messages,
|
|
98
|
+
});
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
return output;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const validate =
|
|
105
|
+
(report: (property: string | null) => (msg: string) => false) =>
|
|
106
|
+
(type: "boolean" | "bigint" | "number" | "string" | "array") =>
|
|
107
|
+
(tagList: IMetadataTypeTag[]): boolean => {
|
|
108
|
+
let success: boolean = true;
|
|
109
|
+
for (const tag of tagList)
|
|
110
|
+
if (tag.target !== type) {
|
|
111
|
+
success &&= report(null)(
|
|
112
|
+
`target must constains ${type} type`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
tagList.forEach((tag, i) => {
|
|
117
|
+
if (tag.exclusive === false) return;
|
|
118
|
+
else if (tag.exclusive === true) {
|
|
119
|
+
const some: boolean = tagList.some(
|
|
120
|
+
(opposite, j) => i !== j && opposite.kind === tag.kind,
|
|
121
|
+
);
|
|
122
|
+
if (some === true)
|
|
123
|
+
success &&= report(null)(
|
|
124
|
+
`kind '${tag.kind}' can't be duplicated`,
|
|
125
|
+
);
|
|
126
|
+
} else if (Array.isArray(tag.exclusive)) {
|
|
127
|
+
const some: IMetadataTypeTag | undefined = tagList.find(
|
|
128
|
+
(opposite, j) =>
|
|
129
|
+
i !== j &&
|
|
130
|
+
opposite.kind === tag.kind &&
|
|
131
|
+
(tag.exclusive as string[]).includes(opposite.name),
|
|
132
|
+
);
|
|
133
|
+
if (some !== undefined)
|
|
134
|
+
success ??= report(null)(
|
|
135
|
+
`kind '${tag.kind}' can't be used with '${some.name}'`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return success;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const validate_property =
|
|
143
|
+
(report: (property: string | null) => (msg: string) => false) =>
|
|
144
|
+
(key: string, value: Metadata): boolean => {
|
|
145
|
+
if (
|
|
146
|
+
// TARGET
|
|
147
|
+
key === "target" &&
|
|
148
|
+
(value.constants.length !== 1 ||
|
|
149
|
+
value.constants[0]!.values.length !== value.size() ||
|
|
150
|
+
value.constants[0]!.values.some(
|
|
151
|
+
(v) =>
|
|
152
|
+
v !== "boolean" &&
|
|
153
|
+
v !== "bigint" &&
|
|
154
|
+
v !== "number" &&
|
|
155
|
+
v !== "string" &&
|
|
156
|
+
v !== "array",
|
|
157
|
+
))
|
|
158
|
+
)
|
|
159
|
+
return report(key)(
|
|
160
|
+
`must be one of 'boolean', 'bigint', 'number', 'string', 'array'`,
|
|
161
|
+
);
|
|
162
|
+
else if (
|
|
163
|
+
// KIND
|
|
164
|
+
key === "kind" &&
|
|
165
|
+
(value.size() !== 1 ||
|
|
166
|
+
value.constants.length !== 1 ||
|
|
167
|
+
value.constants[0]!.type !== "string" ||
|
|
168
|
+
value.constants[0]!.values.length !== 1)
|
|
169
|
+
)
|
|
170
|
+
return report(key)("must be a string literal type");
|
|
171
|
+
else if (
|
|
172
|
+
// VALUE
|
|
173
|
+
key === "value" &&
|
|
174
|
+
(value.size() > 1 ||
|
|
175
|
+
(value.size() !== 0 &&
|
|
176
|
+
(value.constants.length !== 1 ||
|
|
177
|
+
value.constants[0]!.values.length !== 1)))
|
|
178
|
+
)
|
|
179
|
+
return report(key)(
|
|
180
|
+
"must be a constant literal type or undefined value",
|
|
181
|
+
);
|
|
182
|
+
else if (key === "exclusive")
|
|
183
|
+
return get_exclusive(report)(key)(value) !== null;
|
|
184
|
+
else if (key === "validate") {
|
|
185
|
+
//----
|
|
186
|
+
// VALIDATE
|
|
187
|
+
//----
|
|
188
|
+
// UNDEFINED CASE
|
|
189
|
+
if (
|
|
190
|
+
value.size() === 0 &&
|
|
191
|
+
value.isRequired() === false &&
|
|
192
|
+
value.nullable === false
|
|
193
|
+
)
|
|
194
|
+
return true;
|
|
195
|
+
|
|
196
|
+
// STRING CASE
|
|
197
|
+
if (
|
|
198
|
+
value.size() === 1 &&
|
|
199
|
+
value.constants.length === 1 &&
|
|
200
|
+
value.constants[0]!.type === "string" &&
|
|
201
|
+
(value.constants[0]!.values.length === 1) === true
|
|
202
|
+
)
|
|
203
|
+
return true;
|
|
204
|
+
|
|
205
|
+
// RECORD<TARGET, STRING>
|
|
206
|
+
const target: string[] | undefined =
|
|
207
|
+
value.objects[0]?.properties
|
|
208
|
+
.map((p) => p.key.getSoleLiteral()!)
|
|
209
|
+
.filter((str) => str !== null) as string[] | undefined;
|
|
210
|
+
if (target === undefined)
|
|
211
|
+
return report("target")(
|
|
212
|
+
`must be one of 'boolean', 'bigint', 'number', 'string', 'array'`,
|
|
213
|
+
);
|
|
214
|
+
const variadic: boolean =
|
|
215
|
+
value.size() === 1 &&
|
|
216
|
+
value.objects.length === 1 &&
|
|
217
|
+
value.objects[0]!.properties.every(
|
|
218
|
+
(vp) =>
|
|
219
|
+
vp.value.size() === 1 &&
|
|
220
|
+
vp.value.isRequired() &&
|
|
221
|
+
vp.value.nullable === false &&
|
|
222
|
+
vp.value.constants.length === 1 &&
|
|
223
|
+
vp.value.constants[0]!.type === "string" &&
|
|
224
|
+
vp.value.constants[0]!.values.length === 1 &&
|
|
225
|
+
target.includes(vp.key.getSoleLiteral()!),
|
|
226
|
+
);
|
|
227
|
+
if (variadic === false)
|
|
228
|
+
return report(key)(
|
|
229
|
+
`must be a string literal type or Record<Target, string> type.`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
return true;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const create_metadata_type_tag =
|
|
236
|
+
(report: (property: string | null) => (msg: string) => false) =>
|
|
237
|
+
(obj: MetadataObject): ITypeTag | null => {
|
|
238
|
+
const find = (key: string): MetadataProperty | undefined =>
|
|
239
|
+
obj.properties[0]?.value.objects[0]?.properties.find(
|
|
240
|
+
(p) => p.key.getSoleLiteral() === key,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const target = find("target")!.value.constants[0]!
|
|
244
|
+
.values as ITypeTag["target"];
|
|
245
|
+
const kind: string = find("kind")!.value.constants[0]!
|
|
246
|
+
.values[0] as string;
|
|
247
|
+
const value: boolean | bigint | number | string | undefined =
|
|
248
|
+
find("value")?.value.constants[0]?.values[0];
|
|
249
|
+
const exclusive: string[] | boolean | null = get_exclusive(report)(
|
|
250
|
+
"exclusive",
|
|
251
|
+
)(find("exclusive")?.value);
|
|
252
|
+
if (exclusive === null) return null;
|
|
253
|
+
|
|
254
|
+
const validate: Record<string, string> = (() => {
|
|
255
|
+
const validate = find("validate")?.value;
|
|
256
|
+
if (!validate || validate.size() === 0) return {};
|
|
257
|
+
else if (validate.constants.length)
|
|
258
|
+
return Object.fromEntries(
|
|
259
|
+
target.map((t) => [
|
|
260
|
+
t,
|
|
261
|
+
validate.constants[0]!.values[0] as string,
|
|
262
|
+
]),
|
|
263
|
+
);
|
|
264
|
+
return Object.fromEntries(
|
|
265
|
+
validate.objects[0]!.properties.map((p) => [
|
|
266
|
+
p.key.getSoleLiteral()!,
|
|
267
|
+
p.value.constants[0]!.values[0]! as string,
|
|
268
|
+
]),
|
|
269
|
+
);
|
|
270
|
+
})();
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
name: obj.name,
|
|
274
|
+
target,
|
|
275
|
+
kind,
|
|
276
|
+
value,
|
|
277
|
+
validate,
|
|
278
|
+
exclusive: exclusive ?? false,
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const get_exclusive =
|
|
283
|
+
(report: (property: string | null) => (msg: string) => false) =>
|
|
284
|
+
(key: string) =>
|
|
285
|
+
(value: Metadata | undefined): boolean | string[] | null => {
|
|
286
|
+
if (value === undefined) return false;
|
|
287
|
+
else if (
|
|
288
|
+
value.size() === 1 &&
|
|
289
|
+
value.constants.length === 1 &&
|
|
290
|
+
value.constants[0]!.type === "boolean" &&
|
|
291
|
+
value.constants[0]!.values.length === 1
|
|
292
|
+
)
|
|
293
|
+
return value.constants[0]!.values[0]! as boolean;
|
|
294
|
+
else if (
|
|
295
|
+
value.size() === 1 &&
|
|
296
|
+
value.tuples.length === 1 &&
|
|
297
|
+
value.tuples[0]!.type.elements.every(
|
|
298
|
+
(elem) =>
|
|
299
|
+
elem.size() === 1 &&
|
|
300
|
+
elem.constants.length === 1 &&
|
|
301
|
+
elem.constants[0]!.type === "string" &&
|
|
302
|
+
elem.constants[0]!.values.length === 1,
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
return value.tuples[0]!.type.elements.map(
|
|
306
|
+
(elem) => elem.constants[0]!.values[0]! as string,
|
|
307
|
+
);
|
|
308
|
+
report(key)(
|
|
309
|
+
"must a boolean literal type or a tuple of string literal types.",
|
|
310
|
+
);
|
|
311
|
+
return null;
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
interface ITypeTag {
|
|
316
|
+
name: string;
|
|
317
|
+
target: Array<"bigint" | "number" | "string" | "array">;
|
|
318
|
+
kind: string;
|
|
319
|
+
value?: boolean | bigint | number | string;
|
|
320
|
+
validate: Record<string, string>;
|
|
321
|
+
exclusive: boolean | string[];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const ESSENTIAL_FIELDS = ["target", "kind", "value"];
|
|
325
|
+
const FIELDS = [...ESSENTIAL_FIELDS, "validate", "exclusive"];
|