@atscript/typescript 0.1.33 → 0.1.35
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/LICENSE +1 -1
- package/dist/cli.cjs +607 -126
- package/dist/index.cjs +171 -778
- package/dist/index.mjs +163 -770
- package/dist/json-schema-0UUPoHud.mjs +952 -0
- package/dist/json-schema-S5-XAOrR.cjs +1030 -0
- package/dist/utils.cjs +80 -945
- package/dist/utils.d.ts +64 -6
- package/dist/utils.mjs +58 -923
- package/package.json +11 -6
package/dist/utils.cjs
CHANGED
|
@@ -1,915 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
const require_json_schema = require('./json-schema-S5-XAOrR.cjs');
|
|
2
3
|
|
|
3
|
-
//#region packages/typescript/src/validator.ts
|
|
4
|
-
function _define_property(obj, key, value) {
|
|
5
|
-
if (key in obj) Object.defineProperty(obj, key, {
|
|
6
|
-
value,
|
|
7
|
-
enumerable: true,
|
|
8
|
-
configurable: true,
|
|
9
|
-
writable: true
|
|
10
|
-
});
|
|
11
|
-
else obj[key] = value;
|
|
12
|
-
return obj;
|
|
13
|
-
}
|
|
14
|
-
const regexCache = new Map();
|
|
15
|
-
var Validator = class {
|
|
16
|
-
isLimitExceeded() {
|
|
17
|
-
if (this.stackErrors.length > 0) {
|
|
18
|
-
const top = this.stackErrors[this.stackErrors.length - 1];
|
|
19
|
-
return top !== null && top.length >= this.opts.errorLimit;
|
|
20
|
-
}
|
|
21
|
-
return this.errors.length >= this.opts.errorLimit;
|
|
22
|
-
}
|
|
23
|
-
push(name) {
|
|
24
|
-
this.stackPath.push(name);
|
|
25
|
-
this.stackErrors.push(null);
|
|
26
|
-
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
27
|
-
}
|
|
28
|
-
pop(saveErrors) {
|
|
29
|
-
this.stackPath.pop();
|
|
30
|
-
const popped = this.stackErrors.pop();
|
|
31
|
-
if (saveErrors && popped !== null && popped !== undefined && popped.length > 0) for (const err of popped) this.error(err.message, err.path, err.details);
|
|
32
|
-
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
33
|
-
return popped;
|
|
34
|
-
}
|
|
35
|
-
clear() {
|
|
36
|
-
this.stackErrors[this.stackErrors.length - 1] = null;
|
|
37
|
-
}
|
|
38
|
-
error(message, path, details) {
|
|
39
|
-
let errors = this.stackErrors[this.stackErrors.length - 1];
|
|
40
|
-
if (!errors) if (this.stackErrors.length > 0) {
|
|
41
|
-
errors = [];
|
|
42
|
-
this.stackErrors[this.stackErrors.length - 1] = errors;
|
|
43
|
-
} else errors = this.errors;
|
|
44
|
-
const error = {
|
|
45
|
-
path: path || this.cachedPath,
|
|
46
|
-
message
|
|
47
|
-
};
|
|
48
|
-
if (details?.length) error.details = details;
|
|
49
|
-
errors.push(error);
|
|
50
|
-
}
|
|
51
|
-
throw() {
|
|
52
|
-
throw new ValidatorError(this.errors);
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Validates a value against the type definition.
|
|
56
|
-
*
|
|
57
|
-
* Acts as a TypeScript type guard — when it returns `true`, the value
|
|
58
|
-
* is narrowed to `DataType`.
|
|
59
|
-
*
|
|
60
|
-
* @param value - The value to validate.
|
|
61
|
-
* @param safe - If `true`, returns `false` on failure instead of throwing.
|
|
62
|
-
* @returns `true` if the value matches the type definition.
|
|
63
|
-
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
64
|
-
*/ validate(value, safe, context) {
|
|
65
|
-
this.errors = [];
|
|
66
|
-
this.stackErrors = [];
|
|
67
|
-
this.stackPath = [""];
|
|
68
|
-
this.cachedPath = "";
|
|
69
|
-
this.context = context;
|
|
70
|
-
const passed = this.validateSafe(this.def, value);
|
|
71
|
-
this.pop(!passed);
|
|
72
|
-
this.context = undefined;
|
|
73
|
-
if (!passed) {
|
|
74
|
-
if (safe) return false;
|
|
75
|
-
this.throw();
|
|
76
|
-
}
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
validateSafe(def, value) {
|
|
80
|
-
if (this.isLimitExceeded()) return false;
|
|
81
|
-
if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
|
|
82
|
-
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.cachedPath);
|
|
83
|
-
if (def.optional && value === undefined) return true;
|
|
84
|
-
for (const plugin of this.opts.plugins) {
|
|
85
|
-
const result = plugin(this, def, value);
|
|
86
|
-
if (result === false || result === true) return result;
|
|
87
|
-
}
|
|
88
|
-
return this.validateAnnotatedType(def, value);
|
|
89
|
-
}
|
|
90
|
-
get path() {
|
|
91
|
-
return this.cachedPath;
|
|
92
|
-
}
|
|
93
|
-
validateAnnotatedType(def, value) {
|
|
94
|
-
switch (def.type.kind) {
|
|
95
|
-
case "": {
|
|
96
|
-
if (def.type.designType === "phantom") return true;
|
|
97
|
-
return this.validatePrimitive(def, value);
|
|
98
|
-
}
|
|
99
|
-
case "object": return this.validateObject(def, value);
|
|
100
|
-
case "array": return this.validateArray(def, value);
|
|
101
|
-
case "union": return this.validateUnion(def, value);
|
|
102
|
-
case "intersection": return this.validateIntersection(def, value);
|
|
103
|
-
case "tuple": return this.validateTuple(def, value);
|
|
104
|
-
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
validateUnion(def, value) {
|
|
108
|
-
let i = 0;
|
|
109
|
-
const popped = [];
|
|
110
|
-
for (const item of def.type.items) {
|
|
111
|
-
this.push(`[${item.type.kind || item.type.designType}(${i})]`);
|
|
112
|
-
if (this.validateSafe(item, value)) {
|
|
113
|
-
this.pop(false);
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
|
-
const errors = this.pop(false);
|
|
117
|
-
if (errors) popped.push(...errors);
|
|
118
|
-
i++;
|
|
119
|
-
}
|
|
120
|
-
this.clear();
|
|
121
|
-
const expected = def.type.items.map((item, i$1) => `[${item.type.kind || item.type.designType}(${i$1})]`).join(", ");
|
|
122
|
-
this.error(`Value does not match any of the allowed types: ${expected}`, undefined, popped);
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
validateIntersection(def, value) {
|
|
126
|
-
for (const item of def.type.items) if (!this.validateSafe(item, value)) return false;
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
validateTuple(def, value) {
|
|
130
|
-
if (!Array.isArray(value) || value.length !== def.type.items.length) {
|
|
131
|
-
this.error(`Expected array of length ${def.type.items.length}`);
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
let i = 0;
|
|
135
|
-
for (const item of def.type.items) {
|
|
136
|
-
this.push(String(i));
|
|
137
|
-
if (!this.validateSafe(item, value[i])) {
|
|
138
|
-
this.pop(true);
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
this.pop(false);
|
|
142
|
-
i++;
|
|
143
|
-
}
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
|
-
validateArray(def, value) {
|
|
147
|
-
if (!Array.isArray(value)) {
|
|
148
|
-
this.error("Expected array");
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
const minLength = def.metadata.get("expect.minLength");
|
|
152
|
-
if (minLength) {
|
|
153
|
-
const length = typeof minLength === "number" ? minLength : minLength.length;
|
|
154
|
-
if (value.length < length) {
|
|
155
|
-
const message = typeof minLength === "object" && minLength.message ? minLength.message : `Expected minimum length of ${length} items, got ${value.length} items`;
|
|
156
|
-
this.error(message);
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
const maxLength = def.metadata.get("expect.maxLength");
|
|
161
|
-
if (maxLength) {
|
|
162
|
-
const length = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
163
|
-
if (value.length > length) {
|
|
164
|
-
const message = typeof maxLength === "object" && maxLength.message ? maxLength.message : `Expected maximum length of ${length} items, got ${value.length} items`;
|
|
165
|
-
this.error(message);
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
const uniqueItems = def.metadata.get("expect.array.uniqueItems");
|
|
170
|
-
if (uniqueItems) {
|
|
171
|
-
const separator = "▼↩";
|
|
172
|
-
const seen = new Set();
|
|
173
|
-
const keyProps = new Set();
|
|
174
|
-
if (def.type.of.type.kind === "object") {
|
|
175
|
-
for (const [key, val] of def.type.of.type.props.entries()) if (val.metadata.get("expect.array.key")) keyProps.add(key);
|
|
176
|
-
}
|
|
177
|
-
for (let idx = 0; idx < value.length; idx++) {
|
|
178
|
-
const item = value[idx];
|
|
179
|
-
let key;
|
|
180
|
-
if (keyProps.size > 0) {
|
|
181
|
-
key = "";
|
|
182
|
-
for (const prop of keyProps) key += JSON.stringify(item[prop]) + separator;
|
|
183
|
-
} else key = JSON.stringify(item);
|
|
184
|
-
if (seen.has(key)) {
|
|
185
|
-
this.push(String(idx));
|
|
186
|
-
this.error(uniqueItems.message || "Duplicate items are not allowed");
|
|
187
|
-
this.pop(true);
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
seen.add(key);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
let i = 0;
|
|
194
|
-
let passed = true;
|
|
195
|
-
for (const item of value) {
|
|
196
|
-
this.push(String(i));
|
|
197
|
-
if (!this.validateSafe(def.type.of, item)) {
|
|
198
|
-
passed = false;
|
|
199
|
-
this.pop(true);
|
|
200
|
-
if (this.isLimitExceeded()) return false;
|
|
201
|
-
} else this.pop(false);
|
|
202
|
-
i++;
|
|
203
|
-
}
|
|
204
|
-
return passed;
|
|
205
|
-
}
|
|
206
|
-
validateObject(def, value) {
|
|
207
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
208
|
-
this.error("Expected object");
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
let passed = true;
|
|
212
|
-
const valueKeys = new Set(Object.keys(value));
|
|
213
|
-
const typeKeys = new Set();
|
|
214
|
-
let skipList;
|
|
215
|
-
if (this.opts.skipList) {
|
|
216
|
-
const path = this.stackPath.length > 1 ? `${this.cachedPath}.` : "";
|
|
217
|
-
for (const item of this.opts.skipList) if (item.startsWith(path)) {
|
|
218
|
-
const key = item.slice(path.length);
|
|
219
|
-
if (!skipList) skipList = new Set();
|
|
220
|
-
skipList.add(key);
|
|
221
|
-
valueKeys.delete(key);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
let partialFunctionMatched = false;
|
|
225
|
-
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.cachedPath);
|
|
226
|
-
for (const [key, item] of def.type.props.entries()) {
|
|
227
|
-
if (skipList && skipList.has(key) || isPhantomType(item)) continue;
|
|
228
|
-
typeKeys.add(key);
|
|
229
|
-
if (value[key] === undefined) {
|
|
230
|
-
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
231
|
-
}
|
|
232
|
-
this.push(key);
|
|
233
|
-
if (this.validateSafe(item, value[key])) this.pop(false);
|
|
234
|
-
else {
|
|
235
|
-
passed = false;
|
|
236
|
-
this.pop(true);
|
|
237
|
-
if (this.isLimitExceeded()) return false;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
for (const key of valueKeys)
|
|
241
|
-
/** matched patterns for unknown keys */ if (!typeKeys.has(key)) {
|
|
242
|
-
const matched = [];
|
|
243
|
-
for (const { pattern, def: propDef } of def.type.propsPatterns) if (pattern.test(key)) matched.push({
|
|
244
|
-
pattern,
|
|
245
|
-
def: propDef
|
|
246
|
-
});
|
|
247
|
-
if (matched.length > 0) {
|
|
248
|
-
this.push(key);
|
|
249
|
-
let keyPassed = false;
|
|
250
|
-
for (const { def: propDef } of matched) {
|
|
251
|
-
if (this.validateSafe(propDef, value[key])) {
|
|
252
|
-
keyPassed = true;
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
this.clear();
|
|
256
|
-
}
|
|
257
|
-
if (!keyPassed) {
|
|
258
|
-
this.validateSafe(matched[0].def, value[key]);
|
|
259
|
-
this.pop(true);
|
|
260
|
-
passed = false;
|
|
261
|
-
if (this.isLimitExceeded()) return false;
|
|
262
|
-
} else this.pop(false);
|
|
263
|
-
} else if (this.opts.unknownProps !== "ignore") {
|
|
264
|
-
if (this.opts.unknownProps === "error") {
|
|
265
|
-
this.push(key);
|
|
266
|
-
this.error(`Unexpected property`);
|
|
267
|
-
this.pop(true);
|
|
268
|
-
if (this.isLimitExceeded()) return false;
|
|
269
|
-
passed = false;
|
|
270
|
-
} else if (this.opts.unknownProps === "strip") delete value[key];
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return passed;
|
|
274
|
-
}
|
|
275
|
-
validatePrimitive(def, value) {
|
|
276
|
-
if (def.type.value !== undefined) {
|
|
277
|
-
if (value !== def.type.value) {
|
|
278
|
-
this.error(`Expected ${def.type.value}, got ${value}`);
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
return true;
|
|
282
|
-
}
|
|
283
|
-
const typeOfValue = Array.isArray(value) ? "array" : typeof value;
|
|
284
|
-
switch (def.type.designType) {
|
|
285
|
-
case "never": {
|
|
286
|
-
this.error(`This type is impossible, must be an internal problem`);
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
|
-
case "any": return true;
|
|
290
|
-
case "string": {
|
|
291
|
-
if (typeOfValue !== def.type.designType) {
|
|
292
|
-
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
293
|
-
return false;
|
|
294
|
-
}
|
|
295
|
-
return this.validateString(def, value);
|
|
296
|
-
}
|
|
297
|
-
case "number": {
|
|
298
|
-
if (typeOfValue !== def.type.designType) {
|
|
299
|
-
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
|
-
return this.validateNumber(def, value);
|
|
303
|
-
}
|
|
304
|
-
case "boolean": {
|
|
305
|
-
if (typeOfValue !== def.type.designType) {
|
|
306
|
-
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
|
-
return this.validateBoolean(def, value);
|
|
310
|
-
}
|
|
311
|
-
case "undefined": {
|
|
312
|
-
if (value !== undefined) {
|
|
313
|
-
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
314
|
-
return false;
|
|
315
|
-
}
|
|
316
|
-
return true;
|
|
317
|
-
}
|
|
318
|
-
case "null": {
|
|
319
|
-
if (value !== null) {
|
|
320
|
-
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
return true;
|
|
324
|
-
}
|
|
325
|
-
default: throw new Error(`Unknown type "${def.type.designType}"`);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
validateString(def, value) {
|
|
329
|
-
const filled = def.metadata.get("meta.required");
|
|
330
|
-
if (filled) {
|
|
331
|
-
if (value.trim().length === 0) {
|
|
332
|
-
const message = typeof filled === "object" && filled.message ? filled.message : `Must not be empty`;
|
|
333
|
-
this.error(message);
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
const minLength = def.metadata.get("expect.minLength");
|
|
338
|
-
if (minLength) {
|
|
339
|
-
const length = typeof minLength === "number" ? minLength : minLength.length;
|
|
340
|
-
if (value.length < length) {
|
|
341
|
-
const message = typeof minLength === "object" && minLength.message ? minLength.message : `Expected minimum length of ${length} characters, got ${value.length} characters`;
|
|
342
|
-
this.error(message);
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
const maxLength = def.metadata.get("expect.maxLength");
|
|
347
|
-
if (maxLength) {
|
|
348
|
-
const length = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
349
|
-
if (value.length > length) {
|
|
350
|
-
const message = typeof maxLength === "object" && maxLength.message ? maxLength.message : `Expected maximum length of ${length} characters, got ${value.length} characters`;
|
|
351
|
-
this.error(message);
|
|
352
|
-
return false;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
const patterns = def.metadata.get("expect.pattern");
|
|
356
|
-
for (const { pattern, flags, message } of patterns || []) {
|
|
357
|
-
if (!pattern) continue;
|
|
358
|
-
const cacheKey = `${pattern}//${flags || ""}`;
|
|
359
|
-
let regex = regexCache.get(cacheKey);
|
|
360
|
-
if (!regex) {
|
|
361
|
-
regex = new RegExp(pattern, flags);
|
|
362
|
-
regexCache.set(cacheKey, regex);
|
|
363
|
-
}
|
|
364
|
-
if (!regex.test(value)) {
|
|
365
|
-
this.error(message || `Value is expected to match pattern "${pattern}"`);
|
|
366
|
-
return false;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
return true;
|
|
370
|
-
}
|
|
371
|
-
validateNumber(def, value) {
|
|
372
|
-
const int = def.metadata.get("expect.int");
|
|
373
|
-
if (int && value % 1 !== 0) {
|
|
374
|
-
const message = typeof int === "object" && int.message ? int.message : `Expected integer, got ${value}`;
|
|
375
|
-
this.error(message);
|
|
376
|
-
return false;
|
|
377
|
-
}
|
|
378
|
-
const min = def.metadata.get("expect.min");
|
|
379
|
-
if (min) {
|
|
380
|
-
const minValue = typeof min === "number" ? min : min.minValue;
|
|
381
|
-
if (value < minValue) {
|
|
382
|
-
const message = typeof min === "object" && min.message ? min.message : `Expected minimum ${minValue}, got ${value}`;
|
|
383
|
-
this.error(message);
|
|
384
|
-
return false;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
const max = def.metadata.get("expect.max");
|
|
388
|
-
if (max) {
|
|
389
|
-
const maxValue = typeof max === "number" ? max : max.maxValue;
|
|
390
|
-
if (value > maxValue) {
|
|
391
|
-
const message = typeof max === "object" && max.message ? max.message : `Expected maximum ${maxValue}, got ${value}`;
|
|
392
|
-
this.error(message);
|
|
393
|
-
return false;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
return true;
|
|
397
|
-
}
|
|
398
|
-
validateBoolean(def, value) {
|
|
399
|
-
const filled = def.metadata.get("meta.required");
|
|
400
|
-
if (filled) {
|
|
401
|
-
if (value !== true) {
|
|
402
|
-
const message = typeof filled === "object" && filled.message ? filled.message : `Must be checked`;
|
|
403
|
-
this.error(message);
|
|
404
|
-
return false;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
return true;
|
|
408
|
-
}
|
|
409
|
-
constructor(def, opts) {
|
|
410
|
-
_define_property(this, "def", void 0);
|
|
411
|
-
_define_property(this, "opts", void 0);
|
|
412
|
-
/** Validation errors collected during the last {@link validate} call. */ _define_property(this, "errors", void 0);
|
|
413
|
-
_define_property(this, "stackErrors", void 0);
|
|
414
|
-
_define_property(this, "stackPath", void 0);
|
|
415
|
-
_define_property(this, "cachedPath", void 0);
|
|
416
|
-
_define_property(this, "context", void 0);
|
|
417
|
-
this.def = def;
|
|
418
|
-
this.errors = [];
|
|
419
|
-
this.stackErrors = [];
|
|
420
|
-
this.stackPath = [];
|
|
421
|
-
this.cachedPath = "";
|
|
422
|
-
this.opts = {
|
|
423
|
-
partial: false,
|
|
424
|
-
unknownProps: "error",
|
|
425
|
-
errorLimit: 10,
|
|
426
|
-
...opts,
|
|
427
|
-
plugins: opts?.plugins || []
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
var ValidatorError = class extends Error {
|
|
432
|
-
constructor(errors) {
|
|
433
|
-
super(`${errors[0].path ? errors[0].path + ": " : ""}${errors[0].message}`), _define_property(this, "errors", void 0), _define_property(this, "name", void 0), this.errors = errors, this.name = "Validation Error";
|
|
434
|
-
}
|
|
435
|
-
};
|
|
436
|
-
|
|
437
|
-
//#endregion
|
|
438
|
-
//#region packages/typescript/src/annotated-type.ts
|
|
439
|
-
const COMPLEX_KINDS = new Set([
|
|
440
|
-
"union",
|
|
441
|
-
"intersection",
|
|
442
|
-
"tuple"
|
|
443
|
-
]);
|
|
444
|
-
const NON_PRIMITIVE_KINDS = new Set(["array", "object"]);
|
|
445
|
-
/** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
|
|
446
|
-
return new Validator(this, opts);
|
|
447
|
-
}
|
|
448
|
-
function createAnnotatedTypeNode(type, metadata, opts) {
|
|
449
|
-
return {
|
|
450
|
-
__is_atscript_annotated_type: true,
|
|
451
|
-
type,
|
|
452
|
-
metadata,
|
|
453
|
-
validator: validatorMethod,
|
|
454
|
-
id: opts?.id,
|
|
455
|
-
optional: opts?.optional
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
function isAnnotatedType(type) {
|
|
459
|
-
return type && type.__is_atscript_annotated_type;
|
|
460
|
-
}
|
|
461
|
-
function annotate(metadata, key, value, asArray) {
|
|
462
|
-
if (!metadata) return;
|
|
463
|
-
if (asArray) if (metadata.has(key)) {
|
|
464
|
-
const a = metadata.get(key);
|
|
465
|
-
if (Array.isArray(a)) a.push(value);
|
|
466
|
-
else metadata.set(key, [a, value]);
|
|
467
|
-
} else metadata.set(key, [value]);
|
|
468
|
-
else metadata.set(key, value);
|
|
469
|
-
}
|
|
470
|
-
function cloneRefProp(parentType, propName) {
|
|
471
|
-
if (parentType.kind !== "object") return;
|
|
472
|
-
const objType = parentType;
|
|
473
|
-
const existing = objType.props.get(propName);
|
|
474
|
-
if (!existing) return;
|
|
475
|
-
const clonedType = cloneTypeDef(existing.type);
|
|
476
|
-
objType.props.set(propName, createAnnotatedTypeNode(clonedType, new Map(existing.metadata), {
|
|
477
|
-
id: existing.id,
|
|
478
|
-
optional: existing.optional
|
|
479
|
-
}));
|
|
480
|
-
}
|
|
481
|
-
function cloneTypeDef(type) {
|
|
482
|
-
if (type.kind === "object") {
|
|
483
|
-
const obj = type;
|
|
484
|
-
const props = new Map();
|
|
485
|
-
for (const [k, v] of obj.props) props.set(k, createAnnotatedTypeNode(v.type, new Map(v.metadata), {
|
|
486
|
-
id: v.id,
|
|
487
|
-
optional: v.optional
|
|
488
|
-
}));
|
|
489
|
-
return {
|
|
490
|
-
kind: "object",
|
|
491
|
-
props,
|
|
492
|
-
propsPatterns: [...obj.propsPatterns],
|
|
493
|
-
tags: new Set(obj.tags)
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
if (type.kind === "array") {
|
|
497
|
-
const arr = type;
|
|
498
|
-
return {
|
|
499
|
-
kind: "array",
|
|
500
|
-
of: arr.of,
|
|
501
|
-
tags: new Set(arr.tags)
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
if (type.kind === "union" || type.kind === "intersection" || type.kind === "tuple") {
|
|
505
|
-
const complex = type;
|
|
506
|
-
return {
|
|
507
|
-
kind: type.kind,
|
|
508
|
-
items: [...complex.items],
|
|
509
|
-
tags: new Set(complex.tags)
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
return {
|
|
513
|
-
...type,
|
|
514
|
-
tags: new Set(type.tags)
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
function defineAnnotatedType(_kind, base) {
|
|
518
|
-
const kind = _kind || "";
|
|
519
|
-
const type = base?.type || {};
|
|
520
|
-
type.kind = kind;
|
|
521
|
-
if (COMPLEX_KINDS.has(kind)) type.items = [];
|
|
522
|
-
if (kind === "object") {
|
|
523
|
-
type.props = new Map();
|
|
524
|
-
type.propsPatterns = [];
|
|
525
|
-
}
|
|
526
|
-
type.tags = new Set();
|
|
527
|
-
const metadata = base?.metadata || new Map();
|
|
528
|
-
const payload = {
|
|
529
|
-
__is_atscript_annotated_type: true,
|
|
530
|
-
metadata,
|
|
531
|
-
type,
|
|
532
|
-
validator: validatorMethod
|
|
533
|
-
};
|
|
534
|
-
base = base ? Object.assign(base, payload) : payload;
|
|
535
|
-
const handle = {
|
|
536
|
-
$type: base,
|
|
537
|
-
$def: type,
|
|
538
|
-
$metadata: metadata,
|
|
539
|
-
tags(...tags) {
|
|
540
|
-
for (const tag of tags) this.$def.tags.add(tag);
|
|
541
|
-
return this;
|
|
542
|
-
},
|
|
543
|
-
designType(value) {
|
|
544
|
-
this.$def.designType = value;
|
|
545
|
-
return this;
|
|
546
|
-
},
|
|
547
|
-
value(value) {
|
|
548
|
-
this.$def.value = value;
|
|
549
|
-
return this;
|
|
550
|
-
},
|
|
551
|
-
of(value) {
|
|
552
|
-
this.$def.of = value;
|
|
553
|
-
return this;
|
|
554
|
-
},
|
|
555
|
-
item(value) {
|
|
556
|
-
this.$def.items.push(value);
|
|
557
|
-
return this;
|
|
558
|
-
},
|
|
559
|
-
prop(name, value) {
|
|
560
|
-
this.$def.props.set(name, value);
|
|
561
|
-
return this;
|
|
562
|
-
},
|
|
563
|
-
propPattern(pattern, def) {
|
|
564
|
-
this.$def.propsPatterns.push({
|
|
565
|
-
pattern,
|
|
566
|
-
def
|
|
567
|
-
});
|
|
568
|
-
return this;
|
|
569
|
-
},
|
|
570
|
-
optional(value = true) {
|
|
571
|
-
this.$type.optional = value;
|
|
572
|
-
return this;
|
|
573
|
-
},
|
|
574
|
-
copyMetadata(fromMetadata, ignore) {
|
|
575
|
-
for (const [key, value] of fromMetadata.entries()) if (!ignore || !ignore.has(key)) this.$metadata.set(key, value);
|
|
576
|
-
return this;
|
|
577
|
-
},
|
|
578
|
-
refTo(type$1, chain) {
|
|
579
|
-
if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
|
|
580
|
-
let newBase = type$1;
|
|
581
|
-
const typeName = type$1.name || "Unknown";
|
|
582
|
-
if (chain) for (let i = 0; i < chain.length; i++) {
|
|
583
|
-
const c = chain[i];
|
|
584
|
-
if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
|
|
585
|
-
else {
|
|
586
|
-
const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
|
|
587
|
-
throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
|
|
591
|
-
return this;
|
|
592
|
-
},
|
|
593
|
-
annotate(key, value, asArray) {
|
|
594
|
-
annotate(this.$metadata, key, value, asArray);
|
|
595
|
-
return this;
|
|
596
|
-
},
|
|
597
|
-
id(value) {
|
|
598
|
-
this.$type.id = value;
|
|
599
|
-
return this;
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
return handle;
|
|
603
|
-
}
|
|
604
|
-
function isPhantomType(def) {
|
|
605
|
-
return def.type.kind === "" && def.type.designType === "phantom";
|
|
606
|
-
}
|
|
607
|
-
function isAnnotatedTypeOfPrimitive(t) {
|
|
608
|
-
if (NON_PRIMITIVE_KINDS.has(t.type.kind)) return false;
|
|
609
|
-
if (!t.type.kind) return true;
|
|
610
|
-
if (COMPLEX_KINDS.has(t.type.kind)) {
|
|
611
|
-
for (const item of t.type.items) if (!isAnnotatedTypeOfPrimitive(item)) return false;
|
|
612
|
-
return true;
|
|
613
|
-
}
|
|
614
|
-
return false;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
//#endregion
|
|
618
|
-
//#region packages/typescript/src/traverse.ts
|
|
619
|
-
function forAnnotatedType(def, handlers) {
|
|
620
|
-
switch (def.type.kind) {
|
|
621
|
-
case "": {
|
|
622
|
-
const typed = def;
|
|
623
|
-
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
624
|
-
return handlers.final(typed);
|
|
625
|
-
}
|
|
626
|
-
case "object": return handlers.object(def);
|
|
627
|
-
case "array": return handlers.array(def);
|
|
628
|
-
case "union": return handlers.union(def);
|
|
629
|
-
case "intersection": return handlers.intersection(def);
|
|
630
|
-
case "tuple": return handlers.tuple(def);
|
|
631
|
-
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
//#endregion
|
|
636
|
-
//#region packages/typescript/src/json-schema.ts
|
|
637
|
-
/**
|
|
638
|
-
* Detects a discriminator property across union items.
|
|
639
|
-
*
|
|
640
|
-
* Scans all items for object-typed members that share a common property
|
|
641
|
-
* with distinct const/literal values. If exactly one such property exists,
|
|
642
|
-
* it is returned as the discriminator.
|
|
643
|
-
*/ function detectDiscriminator(items) {
|
|
644
|
-
if (items.length < 2) return null;
|
|
645
|
-
for (const item of items) if (item.type.kind !== "object") return null;
|
|
646
|
-
const firstObj = items[0].type;
|
|
647
|
-
const candidates = [];
|
|
648
|
-
for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
|
|
649
|
-
let result = null;
|
|
650
|
-
for (const candidate of candidates) {
|
|
651
|
-
const values = new Set();
|
|
652
|
-
const indexMapping = {};
|
|
653
|
-
let valid = true;
|
|
654
|
-
for (let i = 0; i < items.length; i++) {
|
|
655
|
-
const obj = items[i].type;
|
|
656
|
-
const prop = obj.props.get(candidate);
|
|
657
|
-
if (!prop || prop.type.kind !== "" || prop.type.value === undefined) {
|
|
658
|
-
valid = false;
|
|
659
|
-
break;
|
|
660
|
-
}
|
|
661
|
-
const val = prop.type.value;
|
|
662
|
-
if (values.has(val)) {
|
|
663
|
-
valid = false;
|
|
664
|
-
break;
|
|
665
|
-
}
|
|
666
|
-
values.add(val);
|
|
667
|
-
indexMapping[String(val)] = i;
|
|
668
|
-
}
|
|
669
|
-
if (valid) {
|
|
670
|
-
if (result) return null;
|
|
671
|
-
result = {
|
|
672
|
-
propertyName: candidate,
|
|
673
|
-
indexMapping
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
return result;
|
|
678
|
-
}
|
|
679
|
-
function buildJsonSchema(type) {
|
|
680
|
-
const defs = {};
|
|
681
|
-
let hasDefs = false;
|
|
682
|
-
const buildObject = (d) => {
|
|
683
|
-
const properties = {};
|
|
684
|
-
const required = [];
|
|
685
|
-
for (const [key, val] of d.type.props.entries()) {
|
|
686
|
-
if (isPhantomType(val)) continue;
|
|
687
|
-
properties[key] = build$1(val);
|
|
688
|
-
if (!val.optional) required.push(key);
|
|
689
|
-
}
|
|
690
|
-
const schema$1 = {
|
|
691
|
-
type: "object",
|
|
692
|
-
properties
|
|
693
|
-
};
|
|
694
|
-
if (required.length > 0) schema$1.required = required;
|
|
695
|
-
return schema$1;
|
|
696
|
-
};
|
|
697
|
-
const build$1 = (def) => {
|
|
698
|
-
if (def.id && def.type.kind === "object" && def !== type) {
|
|
699
|
-
const name = def.id;
|
|
700
|
-
if (!defs[name]) {
|
|
701
|
-
hasDefs = true;
|
|
702
|
-
defs[name] = {};
|
|
703
|
-
defs[name] = buildObject(def);
|
|
704
|
-
}
|
|
705
|
-
return { $ref: `#/$defs/${name}` };
|
|
706
|
-
}
|
|
707
|
-
const meta = def.metadata;
|
|
708
|
-
return forAnnotatedType(def, {
|
|
709
|
-
phantom() {
|
|
710
|
-
return {};
|
|
711
|
-
},
|
|
712
|
-
object(d) {
|
|
713
|
-
return buildObject(d);
|
|
714
|
-
},
|
|
715
|
-
array(d) {
|
|
716
|
-
const schema$1 = {
|
|
717
|
-
type: "array",
|
|
718
|
-
items: build$1(d.type.of)
|
|
719
|
-
};
|
|
720
|
-
const minLength = meta.get("expect.minLength");
|
|
721
|
-
if (minLength) schema$1.minItems = typeof minLength === "number" ? minLength : minLength.length;
|
|
722
|
-
const maxLength = meta.get("expect.maxLength");
|
|
723
|
-
if (maxLength) schema$1.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
724
|
-
return schema$1;
|
|
725
|
-
},
|
|
726
|
-
union(d) {
|
|
727
|
-
const disc = detectDiscriminator(d.type.items);
|
|
728
|
-
if (disc) {
|
|
729
|
-
const oneOf = d.type.items.map(build$1);
|
|
730
|
-
const mapping = {};
|
|
731
|
-
for (const [val, idx] of Object.entries(disc.indexMapping)) {
|
|
732
|
-
const item = d.type.items[idx];
|
|
733
|
-
mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
|
|
734
|
-
}
|
|
735
|
-
return {
|
|
736
|
-
oneOf,
|
|
737
|
-
discriminator: {
|
|
738
|
-
propertyName: disc.propertyName,
|
|
739
|
-
mapping
|
|
740
|
-
}
|
|
741
|
-
};
|
|
742
|
-
}
|
|
743
|
-
return { anyOf: d.type.items.map(build$1) };
|
|
744
|
-
},
|
|
745
|
-
intersection(d) {
|
|
746
|
-
return { allOf: d.type.items.map(build$1) };
|
|
747
|
-
},
|
|
748
|
-
tuple(d) {
|
|
749
|
-
return {
|
|
750
|
-
type: "array",
|
|
751
|
-
items: d.type.items.map(build$1),
|
|
752
|
-
additionalItems: false
|
|
753
|
-
};
|
|
754
|
-
},
|
|
755
|
-
final(d) {
|
|
756
|
-
const schema$1 = {};
|
|
757
|
-
if (d.type.value !== undefined) schema$1.const = d.type.value;
|
|
758
|
-
if (d.type.designType && d.type.designType !== "any") {
|
|
759
|
-
schema$1.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
760
|
-
if (schema$1.type === "number" && meta.get("expect.int")) schema$1.type = "integer";
|
|
761
|
-
}
|
|
762
|
-
if (schema$1.type === "string") {
|
|
763
|
-
if (meta.get("meta.required")) schema$1.minLength = 1;
|
|
764
|
-
const minLength = meta.get("expect.minLength");
|
|
765
|
-
if (minLength) schema$1.minLength = typeof minLength === "number" ? minLength : minLength.length;
|
|
766
|
-
const maxLength = meta.get("expect.maxLength");
|
|
767
|
-
if (maxLength) schema$1.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
768
|
-
const patterns = meta.get("expect.pattern");
|
|
769
|
-
if (patterns?.length) if (patterns.length === 1) schema$1.pattern = patterns[0].pattern;
|
|
770
|
-
else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
771
|
-
}
|
|
772
|
-
if (schema$1.type === "number" || schema$1.type === "integer") {
|
|
773
|
-
const min = meta.get("expect.min");
|
|
774
|
-
if (min) schema$1.minimum = typeof min === "number" ? min : min.minValue;
|
|
775
|
-
const max = meta.get("expect.max");
|
|
776
|
-
if (max) schema$1.maximum = typeof max === "number" ? max : max.maxValue;
|
|
777
|
-
}
|
|
778
|
-
return schema$1;
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
};
|
|
782
|
-
const schema = build$1(type);
|
|
783
|
-
if (hasDefs) return {
|
|
784
|
-
...schema,
|
|
785
|
-
$defs: defs
|
|
786
|
-
};
|
|
787
|
-
return schema;
|
|
788
|
-
}
|
|
789
|
-
function fromJsonSchema(schema) {
|
|
790
|
-
const defsSource = schema.$defs || schema.definitions || {};
|
|
791
|
-
const resolved = new Map();
|
|
792
|
-
const convert = (s) => {
|
|
793
|
-
if (!s || Object.keys(s).length === 0) return defineAnnotatedType().designType("any").$type;
|
|
794
|
-
if (s.$ref) {
|
|
795
|
-
const refName = s.$ref.replace(/^#\/(\$defs|definitions)\//, "");
|
|
796
|
-
if (resolved.has(refName)) return resolved.get(refName);
|
|
797
|
-
if (defsSource[refName]) {
|
|
798
|
-
const placeholder = defineAnnotatedType().designType("any").$type;
|
|
799
|
-
resolved.set(refName, placeholder);
|
|
800
|
-
const type = convert(defsSource[refName]);
|
|
801
|
-
resolved.set(refName, type);
|
|
802
|
-
return type;
|
|
803
|
-
}
|
|
804
|
-
throw new Error(`Unresolvable $ref: ${s.$ref}`);
|
|
805
|
-
}
|
|
806
|
-
if ("const" in s) {
|
|
807
|
-
const val = s.const;
|
|
808
|
-
const dt = val === null ? "null" : typeof val;
|
|
809
|
-
return defineAnnotatedType().designType(dt).value(val).$type;
|
|
810
|
-
}
|
|
811
|
-
if (s.enum) {
|
|
812
|
-
const handle = defineAnnotatedType("union");
|
|
813
|
-
for (const val of s.enum) {
|
|
814
|
-
const dt = val === null ? "null" : typeof val;
|
|
815
|
-
handle.item(defineAnnotatedType().designType(dt).value(val).$type);
|
|
816
|
-
}
|
|
817
|
-
return handle.$type;
|
|
818
|
-
}
|
|
819
|
-
if (s.anyOf) {
|
|
820
|
-
const handle = defineAnnotatedType("union");
|
|
821
|
-
for (const item of s.anyOf) handle.item(convert(item));
|
|
822
|
-
return handle.$type;
|
|
823
|
-
}
|
|
824
|
-
if (s.oneOf) {
|
|
825
|
-
const handle = defineAnnotatedType("union");
|
|
826
|
-
for (const item of s.oneOf) handle.item(convert(item));
|
|
827
|
-
return handle.$type;
|
|
828
|
-
}
|
|
829
|
-
if (s.allOf && !s.type) {
|
|
830
|
-
const handle = defineAnnotatedType("intersection");
|
|
831
|
-
for (const item of s.allOf) handle.item(convert(item));
|
|
832
|
-
return handle.$type;
|
|
833
|
-
}
|
|
834
|
-
if (Array.isArray(s.type)) {
|
|
835
|
-
const handle = defineAnnotatedType("union");
|
|
836
|
-
for (const t of s.type) handle.item(convert({
|
|
837
|
-
...s,
|
|
838
|
-
type: t
|
|
839
|
-
}));
|
|
840
|
-
return handle.$type;
|
|
841
|
-
}
|
|
842
|
-
if (s.type === "object") {
|
|
843
|
-
const handle = defineAnnotatedType("object");
|
|
844
|
-
const required = new Set(s.required || []);
|
|
845
|
-
if (s.properties) for (const [key, propSchema] of Object.entries(s.properties)) {
|
|
846
|
-
const propType = convert(propSchema);
|
|
847
|
-
if (!required.has(key)) propType.optional = true;
|
|
848
|
-
handle.prop(key, propType);
|
|
849
|
-
}
|
|
850
|
-
return handle.$type;
|
|
851
|
-
}
|
|
852
|
-
if (s.type === "array") {
|
|
853
|
-
if (Array.isArray(s.items)) {
|
|
854
|
-
const handle$1 = defineAnnotatedType("tuple");
|
|
855
|
-
for (const item of s.items) handle$1.item(convert(item));
|
|
856
|
-
return handle$1.$type;
|
|
857
|
-
}
|
|
858
|
-
const itemType = s.items ? convert(s.items) : defineAnnotatedType().designType("any").$type;
|
|
859
|
-
const handle = defineAnnotatedType("array").of(itemType);
|
|
860
|
-
if (typeof s.minItems === "number") handle.annotate("expect.minLength", { length: s.minItems });
|
|
861
|
-
if (typeof s.maxItems === "number") handle.annotate("expect.maxLength", { length: s.maxItems });
|
|
862
|
-
return handle.$type;
|
|
863
|
-
}
|
|
864
|
-
if (s.type === "string") {
|
|
865
|
-
const handle = defineAnnotatedType().designType("string").tags("string");
|
|
866
|
-
if (typeof s.minLength === "number") handle.annotate("expect.minLength", { length: s.minLength });
|
|
867
|
-
if (typeof s.maxLength === "number") handle.annotate("expect.maxLength", { length: s.maxLength });
|
|
868
|
-
if (s.pattern) handle.annotate("expect.pattern", { pattern: s.pattern }, true);
|
|
869
|
-
if (s.allOf) {
|
|
870
|
-
for (const item of s.allOf) if (item.pattern) handle.annotate("expect.pattern", { pattern: item.pattern }, true);
|
|
871
|
-
}
|
|
872
|
-
return handle.$type;
|
|
873
|
-
}
|
|
874
|
-
if (s.type === "integer") {
|
|
875
|
-
const handle = defineAnnotatedType().designType("number").tags("number");
|
|
876
|
-
handle.annotate("expect.int", true);
|
|
877
|
-
if (typeof s.minimum === "number") handle.annotate("expect.min", { minValue: s.minimum });
|
|
878
|
-
if (typeof s.maximum === "number") handle.annotate("expect.max", { maxValue: s.maximum });
|
|
879
|
-
return handle.$type;
|
|
880
|
-
}
|
|
881
|
-
if (s.type === "number") {
|
|
882
|
-
const handle = defineAnnotatedType().designType("number").tags("number");
|
|
883
|
-
if (typeof s.minimum === "number") handle.annotate("expect.min", { minValue: s.minimum });
|
|
884
|
-
if (typeof s.maximum === "number") handle.annotate("expect.max", { maxValue: s.maximum });
|
|
885
|
-
return handle.$type;
|
|
886
|
-
}
|
|
887
|
-
if (s.type === "boolean") return defineAnnotatedType().designType("boolean").tags("boolean").$type;
|
|
888
|
-
if (s.type === "null") return defineAnnotatedType().designType("null").tags("null").$type;
|
|
889
|
-
return defineAnnotatedType().designType("any").$type;
|
|
890
|
-
};
|
|
891
|
-
return convert(schema);
|
|
892
|
-
}
|
|
893
|
-
function mergeJsonSchemas(types) {
|
|
894
|
-
const mergedDefs = {};
|
|
895
|
-
const schemas = {};
|
|
896
|
-
for (const type of types) {
|
|
897
|
-
const name = type.id;
|
|
898
|
-
if (!name) throw new Error("mergeJsonSchemas: all types must have an id");
|
|
899
|
-
const schema = buildJsonSchema(type);
|
|
900
|
-
if (schema.$defs) {
|
|
901
|
-
for (const [defName, defSchema] of Object.entries(schema.$defs)) if (!mergedDefs[defName]) mergedDefs[defName] = defSchema;
|
|
902
|
-
const { $defs: _,...rest } = schema;
|
|
903
|
-
schemas[name] = rest;
|
|
904
|
-
} else schemas[name] = schema;
|
|
905
|
-
}
|
|
906
|
-
return {
|
|
907
|
-
schemas,
|
|
908
|
-
$defs: mergedDefs
|
|
909
|
-
};
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
//#endregion
|
|
913
4
|
//#region packages/typescript/src/default-value.ts
|
|
914
5
|
/**
|
|
915
6
|
* Attempts to resolve a value from the mode for the given annotated type.
|
|
@@ -924,7 +15,7 @@ function mergeJsonSchemas(types) {
|
|
|
924
15
|
return undefined;
|
|
925
16
|
}
|
|
926
17
|
if (mode === "db") {
|
|
927
|
-
const dbValue = prop.metadata.get("db.default
|
|
18
|
+
const dbValue = prop.metadata.get("db.default");
|
|
928
19
|
if (dbValue !== undefined) {
|
|
929
20
|
const parsed$1 = parseRawValue(dbValue, prop);
|
|
930
21
|
if (parsed$1 !== undefined && prop.validator({ unknownProps: "ignore" }).validate(parsed$1, true)) return { value: parsed$1 };
|
|
@@ -970,13 +61,13 @@ function createDataFromAnnotatedType(type, opts) {
|
|
|
970
61
|
function build(def, path, mode) {
|
|
971
62
|
const resolved = resolveValue(def, path, mode);
|
|
972
63
|
if (resolved !== undefined) return resolved.value;
|
|
973
|
-
return forAnnotatedType(def, {
|
|
64
|
+
return require_json_schema.forAnnotatedType(def, {
|
|
974
65
|
phantom: () => undefined,
|
|
975
66
|
final: (d) => finalDefault(d),
|
|
976
67
|
object: (d) => {
|
|
977
68
|
const data = {};
|
|
978
69
|
for (const [key, prop] of d.type.props.entries()) {
|
|
979
|
-
if (isPhantomType(prop)) continue;
|
|
70
|
+
if (require_json_schema.isPhantomType(prop)) continue;
|
|
980
71
|
const childPath = path ? `${path}.${key}` : key;
|
|
981
72
|
if (prop.optional) {
|
|
982
73
|
if (mode === "example") data[key] = build(prop, childPath, mode);
|
|
@@ -1020,10 +111,11 @@ function throwFeatureDisabled(feature, option, annotation) {
|
|
|
1020
111
|
function flattenAnnotatedType(type, options) {
|
|
1021
112
|
const flatMap = new Map();
|
|
1022
113
|
const skipPhantom = !!options?.excludePhantomTypes;
|
|
114
|
+
const visitedIds = new Set();
|
|
1023
115
|
function addFieldToFlatMap(name, def) {
|
|
1024
116
|
const existing = flatMap.get(name);
|
|
1025
117
|
if (existing) {
|
|
1026
|
-
const flatUnion = defineAnnotatedType("union").copyMetadata(existing.metadata).copyMetadata(def.metadata);
|
|
118
|
+
const flatUnion = require_json_schema.defineAnnotatedType("union").copyMetadata(existing.metadata).copyMetadata(def.metadata);
|
|
1027
119
|
if (existing.__flat_union) existing.type.items.forEach((item) => flatUnion.item(item));
|
|
1028
120
|
else flatUnion.item(existing);
|
|
1029
121
|
flatUnion.item(def);
|
|
@@ -1033,11 +125,20 @@ else flatUnion.item(existing);
|
|
|
1033
125
|
} else flatMap.set(name, def);
|
|
1034
126
|
}
|
|
1035
127
|
function flattenArray(def, name) {
|
|
128
|
+
const resolvedId = def.id;
|
|
129
|
+
if (resolvedId) {
|
|
130
|
+
if (visitedIds.has(resolvedId)) return;
|
|
131
|
+
visitedIds.add(resolvedId);
|
|
132
|
+
}
|
|
1036
133
|
switch (def.type.kind) {
|
|
1037
134
|
case "object": {
|
|
135
|
+
if (!resolvedId && def.id) {
|
|
136
|
+
if (visitedIds.has(def.id)) return;
|
|
137
|
+
visitedIds.add(def.id);
|
|
138
|
+
}
|
|
1038
139
|
const items = Array.from(def.type.props.entries());
|
|
1039
140
|
for (const [key, value] of items) {
|
|
1040
|
-
if (skipPhantom && isPhantomType(value)) continue;
|
|
141
|
+
if (skipPhantom && require_json_schema.isPhantomType(value)) continue;
|
|
1041
142
|
flattenType(value, name ? `${name}.${key}` : key, true);
|
|
1042
143
|
}
|
|
1043
144
|
break;
|
|
@@ -1056,11 +157,28 @@ else flatUnion.item(existing);
|
|
|
1056
157
|
}
|
|
1057
158
|
}
|
|
1058
159
|
function flattenType(def, prefix = "", inComplexTypeOrArray = false) {
|
|
1059
|
-
|
|
160
|
+
let typeId = def.id;
|
|
161
|
+
if (typeId && visitedIds.has(typeId)) {
|
|
162
|
+
addFieldToFlatMap(prefix || "", def);
|
|
163
|
+
if (prefix) options?.onField?.(prefix, def, def.metadata);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (typeId) visitedIds.add(typeId);
|
|
167
|
+
const kind = def.type.kind;
|
|
168
|
+
if (!typeId && def.id) {
|
|
169
|
+
typeId = def.id;
|
|
170
|
+
if (visitedIds.has(typeId)) {
|
|
171
|
+
addFieldToFlatMap(prefix || "", def);
|
|
172
|
+
if (prefix) options?.onField?.(prefix, def, def.metadata);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
visitedIds.add(typeId);
|
|
176
|
+
}
|
|
177
|
+
switch (kind) {
|
|
1060
178
|
case "object": {
|
|
1061
179
|
addFieldToFlatMap(prefix || "", def);
|
|
1062
180
|
for (const [key, value] of def.type.props.entries()) {
|
|
1063
|
-
if (skipPhantom && isPhantomType(value)) continue;
|
|
181
|
+
if (skipPhantom && require_json_schema.isPhantomType(value)) continue;
|
|
1064
182
|
flattenType(value, prefix ? `${prefix}.${key}` : key, inComplexTypeOrArray);
|
|
1065
183
|
}
|
|
1066
184
|
break;
|
|
@@ -1068,12 +186,12 @@ else flatUnion.item(existing);
|
|
|
1068
186
|
case "array": {
|
|
1069
187
|
let typeArray = def;
|
|
1070
188
|
if (!inComplexTypeOrArray) {
|
|
1071
|
-
typeArray = defineAnnotatedType().refTo(def).copyMetadata(def.metadata).$type;
|
|
189
|
+
typeArray = require_json_schema.defineAnnotatedType().refTo(def).copyMetadata(def.metadata).$type;
|
|
1072
190
|
if (def.optional) typeArray.optional = def.optional;
|
|
1073
191
|
if (options?.topLevelArrayTag) typeArray.metadata.set(options.topLevelArrayTag, true);
|
|
1074
192
|
}
|
|
1075
193
|
addFieldToFlatMap(prefix || "", typeArray);
|
|
1076
|
-
if (!isAnnotatedTypeOfPrimitive(typeArray.type.of)) flattenArray(typeArray.type.of, prefix);
|
|
194
|
+
if (!require_json_schema.isAnnotatedTypeOfPrimitive(typeArray.type.of)) flattenArray(typeArray.type.of, prefix);
|
|
1077
195
|
break;
|
|
1078
196
|
}
|
|
1079
197
|
case "intersection":
|
|
@@ -1102,21 +220,32 @@ else flatUnion.item(existing);
|
|
|
1102
220
|
//#region packages/typescript/src/serialize.ts
|
|
1103
221
|
const SERIALIZE_VERSION = 1;
|
|
1104
222
|
function serializeAnnotatedType(type, options) {
|
|
1105
|
-
const
|
|
223
|
+
const visited = new Set();
|
|
224
|
+
const result = serializeNode(type, [], options, visited);
|
|
1106
225
|
result.$v = SERIALIZE_VERSION;
|
|
1107
226
|
return result;
|
|
1108
227
|
}
|
|
1109
|
-
function serializeNode(def, path, options) {
|
|
228
|
+
function serializeNode(def, path, options, visited) {
|
|
229
|
+
if (def.id && visited.has(def.id)) return {
|
|
230
|
+
type: {
|
|
231
|
+
kind: "$ref",
|
|
232
|
+
id: def.id
|
|
233
|
+
},
|
|
234
|
+
metadata: {},
|
|
235
|
+
...def.optional ? { optional: true } : {},
|
|
236
|
+
id: def.id
|
|
237
|
+
};
|
|
238
|
+
if (def.id) visited.add(def.id);
|
|
1110
239
|
const result = {
|
|
1111
|
-
type: serializeTypeDef(def, path, options),
|
|
240
|
+
type: serializeTypeDef(def, path, options, visited),
|
|
1112
241
|
metadata: serializeMetadata(def.metadata, path, def.type.kind, options)
|
|
1113
242
|
};
|
|
1114
243
|
if (def.optional) result.optional = true;
|
|
1115
244
|
if (def.id) result.id = def.id;
|
|
1116
245
|
return result;
|
|
1117
246
|
}
|
|
1118
|
-
function serializeTypeDef(def, path, options) {
|
|
1119
|
-
return forAnnotatedType(def, {
|
|
247
|
+
function serializeTypeDef(def, path, options, visited) {
|
|
248
|
+
return require_json_schema.forAnnotatedType(def, {
|
|
1120
249
|
phantom(d) {
|
|
1121
250
|
return {
|
|
1122
251
|
kind: "",
|
|
@@ -1135,13 +264,13 @@ function serializeTypeDef(def, path, options) {
|
|
|
1135
264
|
},
|
|
1136
265
|
object(d) {
|
|
1137
266
|
const props = {};
|
|
1138
|
-
for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options);
|
|
267
|
+
for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options, visited);
|
|
1139
268
|
const propsPatterns = d.type.propsPatterns.map((pp) => ({
|
|
1140
269
|
pattern: {
|
|
1141
270
|
source: pp.pattern.source,
|
|
1142
271
|
flags: pp.pattern.flags
|
|
1143
272
|
},
|
|
1144
|
-
def: serializeNode(pp.def, path, options)
|
|
273
|
+
def: serializeNode(pp.def, path, options, visited)
|
|
1145
274
|
}));
|
|
1146
275
|
return {
|
|
1147
276
|
kind: "object",
|
|
@@ -1153,28 +282,28 @@ function serializeTypeDef(def, path, options) {
|
|
|
1153
282
|
array(d) {
|
|
1154
283
|
return {
|
|
1155
284
|
kind: "array",
|
|
1156
|
-
of: serializeNode(d.type.of, path, options),
|
|
285
|
+
of: serializeNode(d.type.of, path, options, visited),
|
|
1157
286
|
tags: Array.from(d.type.tags)
|
|
1158
287
|
};
|
|
1159
288
|
},
|
|
1160
289
|
union(d) {
|
|
1161
290
|
return {
|
|
1162
291
|
kind: "union",
|
|
1163
|
-
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
292
|
+
items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
|
|
1164
293
|
tags: Array.from(d.type.tags)
|
|
1165
294
|
};
|
|
1166
295
|
},
|
|
1167
296
|
intersection(d) {
|
|
1168
297
|
return {
|
|
1169
298
|
kind: "intersection",
|
|
1170
|
-
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
299
|
+
items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
|
|
1171
300
|
tags: Array.from(d.type.tags)
|
|
1172
301
|
};
|
|
1173
302
|
},
|
|
1174
303
|
tuple(d) {
|
|
1175
304
|
return {
|
|
1176
305
|
kind: "tuple",
|
|
1177
|
-
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
306
|
+
items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
|
|
1178
307
|
tags: Array.from(d.type.tags)
|
|
1179
308
|
};
|
|
1180
309
|
}
|
|
@@ -1207,14 +336,14 @@ function deserializeAnnotatedType(data) {
|
|
|
1207
336
|
function deserializeNode(data) {
|
|
1208
337
|
const metadata = new Map(Object.entries(data.metadata));
|
|
1209
338
|
const type = deserializeTypeDef(data.type);
|
|
1210
|
-
const result = createAnnotatedTypeNode(type, metadata, {
|
|
339
|
+
const result = require_json_schema.createAnnotatedTypeNode(type, metadata, {
|
|
1211
340
|
optional: data.optional || undefined,
|
|
1212
341
|
id: data.id || undefined
|
|
1213
342
|
});
|
|
1214
343
|
return result;
|
|
1215
344
|
}
|
|
1216
345
|
function deserializeTypeDef(t) {
|
|
1217
|
-
const tags = new Set(t.tags);
|
|
346
|
+
const tags = "tags" in t ? new Set(t.tags) : new Set();
|
|
1218
347
|
switch (t.kind) {
|
|
1219
348
|
case "": {
|
|
1220
349
|
const result = {
|
|
@@ -1251,27 +380,33 @@ function deserializeTypeDef(t) {
|
|
|
1251
380
|
items: t.items.map((item) => deserializeNode(item)),
|
|
1252
381
|
tags
|
|
1253
382
|
};
|
|
383
|
+
case "$ref": return {
|
|
384
|
+
kind: "object",
|
|
385
|
+
props: new Map(),
|
|
386
|
+
propsPatterns: [],
|
|
387
|
+
tags
|
|
388
|
+
};
|
|
1254
389
|
default: throw new Error(`Unknown serialized type kind "${t.kind}"`);
|
|
1255
390
|
}
|
|
1256
391
|
}
|
|
1257
392
|
|
|
1258
393
|
//#endregion
|
|
1259
394
|
exports.SERIALIZE_VERSION = SERIALIZE_VERSION
|
|
1260
|
-
exports.Validator = Validator
|
|
1261
|
-
exports.ValidatorError = ValidatorError
|
|
1262
|
-
exports.annotate = annotate
|
|
1263
|
-
exports.buildJsonSchema = buildJsonSchema
|
|
1264
|
-
exports.cloneRefProp = cloneRefProp
|
|
1265
|
-
exports.createAnnotatedTypeNode = createAnnotatedTypeNode
|
|
395
|
+
exports.Validator = require_json_schema.Validator
|
|
396
|
+
exports.ValidatorError = require_json_schema.ValidatorError
|
|
397
|
+
exports.annotate = require_json_schema.annotate
|
|
398
|
+
exports.buildJsonSchema = require_json_schema.buildJsonSchema
|
|
399
|
+
exports.cloneRefProp = require_json_schema.cloneRefProp
|
|
400
|
+
exports.createAnnotatedTypeNode = require_json_schema.createAnnotatedTypeNode
|
|
1266
401
|
exports.createDataFromAnnotatedType = createDataFromAnnotatedType
|
|
1267
|
-
exports.defineAnnotatedType = defineAnnotatedType
|
|
402
|
+
exports.defineAnnotatedType = require_json_schema.defineAnnotatedType
|
|
1268
403
|
exports.deserializeAnnotatedType = deserializeAnnotatedType
|
|
1269
404
|
exports.flattenAnnotatedType = flattenAnnotatedType
|
|
1270
|
-
exports.forAnnotatedType = forAnnotatedType
|
|
1271
|
-
exports.fromJsonSchema = fromJsonSchema
|
|
1272
|
-
exports.isAnnotatedType = isAnnotatedType
|
|
1273
|
-
exports.isAnnotatedTypeOfPrimitive = isAnnotatedTypeOfPrimitive
|
|
1274
|
-
exports.isPhantomType = isPhantomType
|
|
1275
|
-
exports.mergeJsonSchemas = mergeJsonSchemas
|
|
405
|
+
exports.forAnnotatedType = require_json_schema.forAnnotatedType
|
|
406
|
+
exports.fromJsonSchema = require_json_schema.fromJsonSchema
|
|
407
|
+
exports.isAnnotatedType = require_json_schema.isAnnotatedType
|
|
408
|
+
exports.isAnnotatedTypeOfPrimitive = require_json_schema.isAnnotatedTypeOfPrimitive
|
|
409
|
+
exports.isPhantomType = require_json_schema.isPhantomType
|
|
410
|
+
exports.mergeJsonSchemas = require_json_schema.mergeJsonSchemas
|
|
1276
411
|
exports.serializeAnnotatedType = serializeAnnotatedType
|
|
1277
412
|
exports.throwFeatureDisabled = throwFeatureDisabled
|