@atscript/typescript 0.1.31 → 0.1.33
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/dist/cli.cjs +301 -236
- package/dist/index.cjs +301 -236
- package/dist/index.mjs +301 -236
- package/dist/utils.cjs +160 -143
- package/dist/utils.d.ts +28 -8
- package/dist/utils.mjs +160 -144
- package/package.json +2 -2
- package/skills/atscript-typescript/annotations.md +2 -1
package/dist/utils.mjs
CHANGED
|
@@ -1,22 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
//#region packages/typescript/src/traverse.ts
|
|
3
|
-
function forAnnotatedType(def, handlers) {
|
|
4
|
-
switch (def.type.kind) {
|
|
5
|
-
case "": {
|
|
6
|
-
const typed = def;
|
|
7
|
-
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
8
|
-
return handlers.final(typed);
|
|
9
|
-
}
|
|
10
|
-
case "object": return handlers.object(def);
|
|
11
|
-
case "array": return handlers.array(def);
|
|
12
|
-
case "union": return handlers.union(def);
|
|
13
|
-
case "intersection": return handlers.intersection(def);
|
|
14
|
-
case "tuple": return handlers.tuple(def);
|
|
15
|
-
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
//#endregion
|
|
20
2
|
//#region packages/typescript/src/validator.ts
|
|
21
3
|
function _define_property(obj, key, value) {
|
|
22
4
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -31,28 +13,35 @@ else obj[key] = value;
|
|
|
31
13
|
const regexCache = new Map();
|
|
32
14
|
var Validator = class {
|
|
33
15
|
isLimitExceeded() {
|
|
34
|
-
if (this.stackErrors.length > 0)
|
|
16
|
+
if (this.stackErrors.length > 0) {
|
|
17
|
+
const top = this.stackErrors[this.stackErrors.length - 1];
|
|
18
|
+
return top !== null && top.length >= this.opts.errorLimit;
|
|
19
|
+
}
|
|
35
20
|
return this.errors.length >= this.opts.errorLimit;
|
|
36
21
|
}
|
|
37
22
|
push(name) {
|
|
38
23
|
this.stackPath.push(name);
|
|
39
|
-
this.stackErrors.push(
|
|
24
|
+
this.stackErrors.push(null);
|
|
25
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
40
26
|
}
|
|
41
27
|
pop(saveErrors) {
|
|
42
28
|
this.stackPath.pop();
|
|
43
29
|
const popped = this.stackErrors.pop();
|
|
44
|
-
if (saveErrors && popped
|
|
45
|
-
|
|
46
|
-
});
|
|
30
|
+
if (saveErrors && popped !== null && popped !== undefined && popped.length > 0) for (const err of popped) this.error(err.message, err.path, err.details);
|
|
31
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
47
32
|
return popped;
|
|
48
33
|
}
|
|
49
34
|
clear() {
|
|
50
|
-
this.stackErrors[this.stackErrors.length - 1] =
|
|
35
|
+
this.stackErrors[this.stackErrors.length - 1] = null;
|
|
51
36
|
}
|
|
52
37
|
error(message, path, details) {
|
|
53
|
-
|
|
38
|
+
let errors = this.stackErrors[this.stackErrors.length - 1];
|
|
39
|
+
if (!errors) if (this.stackErrors.length > 0) {
|
|
40
|
+
errors = [];
|
|
41
|
+
this.stackErrors[this.stackErrors.length - 1] = errors;
|
|
42
|
+
} else errors = this.errors;
|
|
54
43
|
const error = {
|
|
55
|
-
path: path || this.
|
|
44
|
+
path: path || this.cachedPath,
|
|
56
45
|
message
|
|
57
46
|
};
|
|
58
47
|
if (details?.length) error.details = details;
|
|
@@ -72,9 +61,10 @@ var Validator = class {
|
|
|
72
61
|
* @returns `true` if the value matches the type definition.
|
|
73
62
|
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
74
63
|
*/ validate(value, safe, context) {
|
|
75
|
-
this.push("");
|
|
76
64
|
this.errors = [];
|
|
77
65
|
this.stackErrors = [];
|
|
66
|
+
this.stackPath = [""];
|
|
67
|
+
this.cachedPath = "";
|
|
78
68
|
this.context = context;
|
|
79
69
|
const passed = this.validateSafe(this.def, value);
|
|
80
70
|
this.pop(!passed);
|
|
@@ -88,7 +78,7 @@ var Validator = class {
|
|
|
88
78
|
validateSafe(def, value) {
|
|
89
79
|
if (this.isLimitExceeded()) return false;
|
|
90
80
|
if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
|
|
91
|
-
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.
|
|
81
|
+
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.cachedPath);
|
|
92
82
|
if (def.optional && value === undefined) return true;
|
|
93
83
|
for (const plugin of this.opts.plugins) {
|
|
94
84
|
const result = plugin(this, def, value);
|
|
@@ -97,18 +87,21 @@ var Validator = class {
|
|
|
97
87
|
return this.validateAnnotatedType(def, value);
|
|
98
88
|
}
|
|
99
89
|
get path() {
|
|
100
|
-
return this.
|
|
90
|
+
return this.cachedPath;
|
|
101
91
|
}
|
|
102
92
|
validateAnnotatedType(def, value) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
93
|
+
switch (def.type.kind) {
|
|
94
|
+
case "": {
|
|
95
|
+
if (def.type.designType === "phantom") return true;
|
|
96
|
+
return this.validatePrimitive(def, value);
|
|
97
|
+
}
|
|
98
|
+
case "object": return this.validateObject(def, value);
|
|
99
|
+
case "array": return this.validateArray(def, value);
|
|
100
|
+
case "union": return this.validateUnion(def, value);
|
|
101
|
+
case "intersection": return this.validateIntersection(def, value);
|
|
102
|
+
case "tuple": return this.validateTuple(def, value);
|
|
103
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
104
|
+
}
|
|
112
105
|
}
|
|
113
106
|
validateUnion(def, value) {
|
|
114
107
|
let i = 0;
|
|
@@ -172,6 +165,30 @@ var Validator = class {
|
|
|
172
165
|
return false;
|
|
173
166
|
}
|
|
174
167
|
}
|
|
168
|
+
const uniqueItems = def.metadata.get("expect.array.uniqueItems");
|
|
169
|
+
if (uniqueItems) {
|
|
170
|
+
const separator = "▼↩";
|
|
171
|
+
const seen = new Set();
|
|
172
|
+
const keyProps = new Set();
|
|
173
|
+
if (def.type.of.type.kind === "object") {
|
|
174
|
+
for (const [key, val] of def.type.of.type.props.entries()) if (val.metadata.get("expect.array.key")) keyProps.add(key);
|
|
175
|
+
}
|
|
176
|
+
for (let idx = 0; idx < value.length; idx++) {
|
|
177
|
+
const item = value[idx];
|
|
178
|
+
let key;
|
|
179
|
+
if (keyProps.size > 0) {
|
|
180
|
+
key = "";
|
|
181
|
+
for (const prop of keyProps) key += JSON.stringify(item[prop]) + separator;
|
|
182
|
+
} else key = JSON.stringify(item);
|
|
183
|
+
if (seen.has(key)) {
|
|
184
|
+
this.push(String(idx));
|
|
185
|
+
this.error(uniqueItems.message || "Duplicate items are not allowed");
|
|
186
|
+
this.pop(true);
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
seen.add(key);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
175
192
|
let i = 0;
|
|
176
193
|
let passed = true;
|
|
177
194
|
for (const item of value) {
|
|
@@ -193,21 +210,20 @@ var Validator = class {
|
|
|
193
210
|
let passed = true;
|
|
194
211
|
const valueKeys = new Set(Object.keys(value));
|
|
195
212
|
const typeKeys = new Set();
|
|
196
|
-
|
|
213
|
+
let skipList;
|
|
197
214
|
if (this.opts.skipList) {
|
|
198
|
-
const path = this.stackPath.length > 1 ? `${this.
|
|
199
|
-
this.opts.skipList.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
});
|
|
215
|
+
const path = this.stackPath.length > 1 ? `${this.cachedPath}.` : "";
|
|
216
|
+
for (const item of this.opts.skipList) if (item.startsWith(path)) {
|
|
217
|
+
const key = item.slice(path.length);
|
|
218
|
+
if (!skipList) skipList = new Set();
|
|
219
|
+
skipList.add(key);
|
|
220
|
+
valueKeys.delete(key);
|
|
221
|
+
}
|
|
206
222
|
}
|
|
207
223
|
let partialFunctionMatched = false;
|
|
208
|
-
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.
|
|
224
|
+
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.cachedPath);
|
|
209
225
|
for (const [key, item] of def.type.props.entries()) {
|
|
210
|
-
if (skipList.has(key) || isPhantomType(item)) continue;
|
|
226
|
+
if (skipList && skipList.has(key) || isPhantomType(item)) continue;
|
|
211
227
|
typeKeys.add(key);
|
|
212
228
|
if (value[key] === undefined) {
|
|
213
229
|
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
@@ -228,19 +244,21 @@ else {
|
|
|
228
244
|
def: propDef
|
|
229
245
|
});
|
|
230
246
|
if (matched.length > 0) {
|
|
247
|
+
this.push(key);
|
|
231
248
|
let keyPassed = false;
|
|
232
|
-
for (const { def:
|
|
233
|
-
this.
|
|
234
|
-
|
|
235
|
-
|
|
249
|
+
for (const { def: propDef } of matched) {
|
|
250
|
+
if (this.validateSafe(propDef, value[key])) {
|
|
251
|
+
keyPassed = true;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
this.clear();
|
|
236
255
|
}
|
|
237
256
|
if (!keyPassed) {
|
|
238
|
-
this.push(key);
|
|
239
257
|
this.validateSafe(matched[0].def, value[key]);
|
|
240
258
|
this.pop(true);
|
|
241
259
|
passed = false;
|
|
242
260
|
if (this.isLimitExceeded()) return false;
|
|
243
|
-
}
|
|
261
|
+
} else this.pop(false);
|
|
244
262
|
} else if (this.opts.unknownProps !== "ignore") {
|
|
245
263
|
if (this.opts.unknownProps === "error") {
|
|
246
264
|
this.push(key);
|
|
@@ -393,11 +411,13 @@ else {
|
|
|
393
411
|
/** Validation errors collected during the last {@link validate} call. */ _define_property(this, "errors", void 0);
|
|
394
412
|
_define_property(this, "stackErrors", void 0);
|
|
395
413
|
_define_property(this, "stackPath", void 0);
|
|
414
|
+
_define_property(this, "cachedPath", void 0);
|
|
396
415
|
_define_property(this, "context", void 0);
|
|
397
416
|
this.def = def;
|
|
398
417
|
this.errors = [];
|
|
399
418
|
this.stackErrors = [];
|
|
400
419
|
this.stackPath = [];
|
|
420
|
+
this.cachedPath = "";
|
|
401
421
|
this.opts = {
|
|
402
422
|
partial: false,
|
|
403
423
|
unknownProps: "error",
|
|
@@ -415,6 +435,25 @@ var ValidatorError = class extends Error {
|
|
|
415
435
|
|
|
416
436
|
//#endregion
|
|
417
437
|
//#region packages/typescript/src/annotated-type.ts
|
|
438
|
+
const COMPLEX_KINDS = new Set([
|
|
439
|
+
"union",
|
|
440
|
+
"intersection",
|
|
441
|
+
"tuple"
|
|
442
|
+
]);
|
|
443
|
+
const NON_PRIMITIVE_KINDS = new Set(["array", "object"]);
|
|
444
|
+
/** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
|
|
445
|
+
return new Validator(this, opts);
|
|
446
|
+
}
|
|
447
|
+
function createAnnotatedTypeNode(type, metadata, opts) {
|
|
448
|
+
return {
|
|
449
|
+
__is_atscript_annotated_type: true,
|
|
450
|
+
type,
|
|
451
|
+
metadata,
|
|
452
|
+
validator: validatorMethod,
|
|
453
|
+
id: opts?.id,
|
|
454
|
+
optional: opts?.optional
|
|
455
|
+
};
|
|
456
|
+
}
|
|
418
457
|
function isAnnotatedType(type) {
|
|
419
458
|
return type && type.__is_atscript_annotated_type;
|
|
420
459
|
}
|
|
@@ -433,32 +472,22 @@ function cloneRefProp(parentType, propName) {
|
|
|
433
472
|
const existing = objType.props.get(propName);
|
|
434
473
|
if (!existing) return;
|
|
435
474
|
const clonedType = cloneTypeDef(existing.type);
|
|
436
|
-
objType.props.set(propName, {
|
|
437
|
-
__is_atscript_annotated_type: true,
|
|
438
|
-
type: clonedType,
|
|
439
|
-
metadata: new Map(existing.metadata),
|
|
475
|
+
objType.props.set(propName, createAnnotatedTypeNode(clonedType, new Map(existing.metadata), {
|
|
440
476
|
id: existing.id,
|
|
441
|
-
optional: existing.optional
|
|
442
|
-
|
|
443
|
-
return new Validator(this, opts);
|
|
444
|
-
}
|
|
445
|
-
});
|
|
477
|
+
optional: existing.optional
|
|
478
|
+
}));
|
|
446
479
|
}
|
|
447
480
|
function cloneTypeDef(type) {
|
|
448
481
|
if (type.kind === "object") {
|
|
449
482
|
const obj = type;
|
|
483
|
+
const props = new Map();
|
|
484
|
+
for (const [k, v] of obj.props) props.set(k, createAnnotatedTypeNode(v.type, new Map(v.metadata), {
|
|
485
|
+
id: v.id,
|
|
486
|
+
optional: v.optional
|
|
487
|
+
}));
|
|
450
488
|
return {
|
|
451
489
|
kind: "object",
|
|
452
|
-
props
|
|
453
|
-
__is_atscript_annotated_type: true,
|
|
454
|
-
type: v.type,
|
|
455
|
-
metadata: new Map(v.metadata),
|
|
456
|
-
id: v.id,
|
|
457
|
-
optional: v.optional,
|
|
458
|
-
validator(opts) {
|
|
459
|
-
return new Validator(this, opts);
|
|
460
|
-
}
|
|
461
|
-
}])),
|
|
490
|
+
props,
|
|
462
491
|
propsPatterns: [...obj.propsPatterns],
|
|
463
492
|
tags: new Set(obj.tags)
|
|
464
493
|
};
|
|
@@ -488,38 +517,24 @@ function defineAnnotatedType(_kind, base) {
|
|
|
488
517
|
const kind = _kind || "";
|
|
489
518
|
const type = base?.type || {};
|
|
490
519
|
type.kind = kind;
|
|
491
|
-
if ([
|
|
492
|
-
"union",
|
|
493
|
-
"intersection",
|
|
494
|
-
"tuple"
|
|
495
|
-
].includes(kind)) type.items = [];
|
|
520
|
+
if (COMPLEX_KINDS.has(kind)) type.items = [];
|
|
496
521
|
if (kind === "object") {
|
|
497
522
|
type.props = new Map();
|
|
498
523
|
type.propsPatterns = [];
|
|
499
524
|
}
|
|
500
525
|
type.tags = new Set();
|
|
501
526
|
const metadata = base?.metadata || new Map();
|
|
502
|
-
|
|
527
|
+
const payload = {
|
|
503
528
|
__is_atscript_annotated_type: true,
|
|
504
529
|
metadata,
|
|
505
530
|
type,
|
|
506
|
-
validator
|
|
507
|
-
return new Validator(this, opts);
|
|
508
|
-
}
|
|
509
|
-
});
|
|
510
|
-
else base = {
|
|
511
|
-
__is_atscript_annotated_type: true,
|
|
512
|
-
metadata,
|
|
513
|
-
type,
|
|
514
|
-
validator(opts) {
|
|
515
|
-
return new Validator(this, opts);
|
|
516
|
-
}
|
|
531
|
+
validator: validatorMethod
|
|
517
532
|
};
|
|
533
|
+
base = base ? Object.assign(base, payload) : payload;
|
|
518
534
|
const handle = {
|
|
519
535
|
$type: base,
|
|
520
536
|
$def: type,
|
|
521
537
|
$metadata: metadata,
|
|
522
|
-
_existingObject: undefined,
|
|
523
538
|
tags(...tags) {
|
|
524
539
|
for (const tag of tags) this.$def.tags.add(tag);
|
|
525
540
|
return this;
|
|
@@ -560,27 +575,18 @@ else base = {
|
|
|
560
575
|
return this;
|
|
561
576
|
},
|
|
562
577
|
refTo(type$1, chain) {
|
|
578
|
+
if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
|
|
563
579
|
let newBase = type$1;
|
|
564
580
|
const typeName = type$1.name || "Unknown";
|
|
565
|
-
if (
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
581
|
+
if (chain) for (let i = 0; i < chain.length; i++) {
|
|
582
|
+
const c = chain[i];
|
|
583
|
+
if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
|
|
584
|
+
else {
|
|
585
|
+
const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
|
|
586
|
+
throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
571
587
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
this.$type = {
|
|
575
|
-
__is_atscript_annotated_type: true,
|
|
576
|
-
type: newBase.type,
|
|
577
|
-
metadata,
|
|
578
|
-
id: newBase.id,
|
|
579
|
-
validator(opts) {
|
|
580
|
-
return new Validator(this, opts);
|
|
581
|
-
}
|
|
582
|
-
};
|
|
583
|
-
} else throw new Error(`${type$1} is not annotated type`);
|
|
588
|
+
}
|
|
589
|
+
this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
|
|
584
590
|
return this;
|
|
585
591
|
},
|
|
586
592
|
annotate(key, value, asArray) {
|
|
@@ -598,19 +604,33 @@ function isPhantomType(def) {
|
|
|
598
604
|
return def.type.kind === "" && def.type.designType === "phantom";
|
|
599
605
|
}
|
|
600
606
|
function isAnnotatedTypeOfPrimitive(t) {
|
|
601
|
-
if (
|
|
607
|
+
if (NON_PRIMITIVE_KINDS.has(t.type.kind)) return false;
|
|
602
608
|
if (!t.type.kind) return true;
|
|
603
|
-
if (
|
|
604
|
-
"union",
|
|
605
|
-
"tuple",
|
|
606
|
-
"intersection"
|
|
607
|
-
].includes(t.type.kind)) {
|
|
609
|
+
if (COMPLEX_KINDS.has(t.type.kind)) {
|
|
608
610
|
for (const item of t.type.items) if (!isAnnotatedTypeOfPrimitive(item)) return false;
|
|
609
611
|
return true;
|
|
610
612
|
}
|
|
611
613
|
return false;
|
|
612
614
|
}
|
|
613
615
|
|
|
616
|
+
//#endregion
|
|
617
|
+
//#region packages/typescript/src/traverse.ts
|
|
618
|
+
function forAnnotatedType(def, handlers) {
|
|
619
|
+
switch (def.type.kind) {
|
|
620
|
+
case "": {
|
|
621
|
+
const typed = def;
|
|
622
|
+
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
623
|
+
return handlers.final(typed);
|
|
624
|
+
}
|
|
625
|
+
case "object": return handlers.object(def);
|
|
626
|
+
case "array": return handlers.array(def);
|
|
627
|
+
case "union": return handlers.union(def);
|
|
628
|
+
case "intersection": return handlers.intersection(def);
|
|
629
|
+
case "tuple": return handlers.tuple(def);
|
|
630
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
614
634
|
//#endregion
|
|
615
635
|
//#region packages/typescript/src/json-schema.ts
|
|
616
636
|
/**
|
|
@@ -625,10 +645,10 @@ function isAnnotatedTypeOfPrimitive(t) {
|
|
|
625
645
|
const firstObj = items[0].type;
|
|
626
646
|
const candidates = [];
|
|
627
647
|
for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
|
|
628
|
-
|
|
648
|
+
let result = null;
|
|
629
649
|
for (const candidate of candidates) {
|
|
630
650
|
const values = new Set();
|
|
631
|
-
const
|
|
651
|
+
const indexMapping = {};
|
|
632
652
|
let valid = true;
|
|
633
653
|
for (let i = 0; i < items.length; i++) {
|
|
634
654
|
const obj = items[i].type;
|
|
@@ -643,19 +663,21 @@ function isAnnotatedTypeOfPrimitive(t) {
|
|
|
643
663
|
break;
|
|
644
664
|
}
|
|
645
665
|
values.add(val);
|
|
646
|
-
|
|
666
|
+
indexMapping[String(val)] = i;
|
|
667
|
+
}
|
|
668
|
+
if (valid) {
|
|
669
|
+
if (result) return null;
|
|
670
|
+
result = {
|
|
671
|
+
propertyName: candidate,
|
|
672
|
+
indexMapping
|
|
673
|
+
};
|
|
647
674
|
}
|
|
648
|
-
if (valid) validCandidates.push({
|
|
649
|
-
propertyName: candidate,
|
|
650
|
-
mapping
|
|
651
|
-
});
|
|
652
675
|
}
|
|
653
|
-
|
|
654
|
-
return null;
|
|
676
|
+
return result;
|
|
655
677
|
}
|
|
656
678
|
function buildJsonSchema(type) {
|
|
657
679
|
const defs = {};
|
|
658
|
-
let
|
|
680
|
+
let hasDefs = false;
|
|
659
681
|
const buildObject = (d) => {
|
|
660
682
|
const properties = {};
|
|
661
683
|
const required = [];
|
|
@@ -672,15 +694,15 @@ function buildJsonSchema(type) {
|
|
|
672
694
|
return schema$1;
|
|
673
695
|
};
|
|
674
696
|
const build$1 = (def) => {
|
|
675
|
-
if (def.id && def.type.kind === "object" &&
|
|
697
|
+
if (def.id && def.type.kind === "object" && def !== type) {
|
|
676
698
|
const name = def.id;
|
|
677
699
|
if (!defs[name]) {
|
|
700
|
+
hasDefs = true;
|
|
678
701
|
defs[name] = {};
|
|
679
702
|
defs[name] = buildObject(def);
|
|
680
703
|
}
|
|
681
704
|
return { $ref: `#/$defs/${name}` };
|
|
682
705
|
}
|
|
683
|
-
isRoot = false;
|
|
684
706
|
const meta = def.metadata;
|
|
685
707
|
return forAnnotatedType(def, {
|
|
686
708
|
phantom() {
|
|
@@ -705,11 +727,9 @@ function buildJsonSchema(type) {
|
|
|
705
727
|
if (disc) {
|
|
706
728
|
const oneOf = d.type.items.map(build$1);
|
|
707
729
|
const mapping = {};
|
|
708
|
-
for (const [val,
|
|
709
|
-
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
730
|
+
for (const [val, idx] of Object.entries(disc.indexMapping)) {
|
|
710
731
|
const item = d.type.items[idx];
|
|
711
|
-
|
|
712
|
-
else mapping[val] = origPath;
|
|
732
|
+
mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
|
|
713
733
|
}
|
|
714
734
|
return {
|
|
715
735
|
oneOf,
|
|
@@ -759,7 +779,7 @@ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ patte
|
|
|
759
779
|
});
|
|
760
780
|
};
|
|
761
781
|
const schema = build$1(type);
|
|
762
|
-
if (
|
|
782
|
+
if (hasDefs) return {
|
|
763
783
|
...schema,
|
|
764
784
|
$defs: defs
|
|
765
785
|
};
|
|
@@ -774,6 +794,8 @@ function fromJsonSchema(schema) {
|
|
|
774
794
|
const refName = s.$ref.replace(/^#\/(\$defs|definitions)\//, "");
|
|
775
795
|
if (resolved.has(refName)) return resolved.get(refName);
|
|
776
796
|
if (defsSource[refName]) {
|
|
797
|
+
const placeholder = defineAnnotatedType().designType("any").$type;
|
|
798
|
+
resolved.set(refName, placeholder);
|
|
777
799
|
const type = convert(defsSource[refName]);
|
|
778
800
|
resolved.set(refName, type);
|
|
779
801
|
return type;
|
|
@@ -1184,16 +1206,10 @@ function deserializeAnnotatedType(data) {
|
|
|
1184
1206
|
function deserializeNode(data) {
|
|
1185
1207
|
const metadata = new Map(Object.entries(data.metadata));
|
|
1186
1208
|
const type = deserializeTypeDef(data.type);
|
|
1187
|
-
const result = {
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
validator(opts) {
|
|
1192
|
-
return new Validator(this, opts);
|
|
1193
|
-
}
|
|
1194
|
-
};
|
|
1195
|
-
if (data.optional) result.optional = true;
|
|
1196
|
-
if (data.id) result.id = data.id;
|
|
1209
|
+
const result = createAnnotatedTypeNode(type, metadata, {
|
|
1210
|
+
optional: data.optional || undefined,
|
|
1211
|
+
id: data.id || undefined
|
|
1212
|
+
});
|
|
1197
1213
|
return result;
|
|
1198
1214
|
}
|
|
1199
1215
|
function deserializeTypeDef(t) {
|
|
@@ -1239,4 +1255,4 @@ function deserializeTypeDef(t) {
|
|
|
1239
1255
|
}
|
|
1240
1256
|
|
|
1241
1257
|
//#endregion
|
|
1242
|
-
export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
|
|
1258
|
+
export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createAnnotatedTypeNode, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/typescript",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
4
|
"description": "Atscript: typescript-gen support.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"annotations",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"vitest": "3.2.4"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@atscript/core": "^0.1.
|
|
67
|
+
"@atscript/core": "^0.1.33"
|
|
68
68
|
},
|
|
69
69
|
"build": [
|
|
70
70
|
{},
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
| `@meta.required` | `message?: string` | Required field. Strings: non-whitespace. Booleans: must be `true` |
|
|
18
18
|
| `@meta.default` | `value: string` | Default value (strings as-is, others parsed as JSON) |
|
|
19
19
|
| `@meta.example` | `value: string` | Example value (strings as-is, others parsed as JSON) |
|
|
20
|
-
| `@expect.array.key` |
|
|
20
|
+
| `@expect.array.key` | `message?: string` | Mark field as key inside array (string/number only, non-optional). Multiple = composite key |
|
|
21
21
|
|
|
22
22
|
### `@expect.*` — Validation Constraints
|
|
23
23
|
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
| `@expect.max` | `maxValue: number`, `message?: string` | number | Maximum value |
|
|
30
30
|
| `@expect.int` | _(none)_ | number | Must be integer |
|
|
31
31
|
| `@expect.pattern` | `pattern: string`, `flags?: string`, `message?: string` | string | Regex validation. **Multiple allowed** (all must pass) |
|
|
32
|
+
| `@expect.array.uniqueItems` | `message?: string` | array | No duplicate items (by key fields or deep equality) |
|
|
32
33
|
|
|
33
34
|
### `@ui.*` — UI / Presentation Hints
|
|
34
35
|
|