@atscript/typescript 0.1.2 → 0.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/dist/cli.cjs +118 -54
- package/dist/index.cjs +118 -54
- package/dist/index.d.ts +8 -2
- package/dist/index.mjs +118 -54
- package/dist/utils.cjs +330 -42
- package/dist/utils.d.ts +286 -12
- package/dist/utils.mjs +324 -42
- package/package.json +2 -2
package/dist/utils.cjs
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
//#region packages/typescript/src/traverse.ts
|
|
4
|
+
function forAnnotatedType(def, handlers) {
|
|
5
|
+
switch (def.type.kind) {
|
|
6
|
+
case "": {
|
|
7
|
+
const typed = def;
|
|
8
|
+
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
9
|
+
return handlers.final(typed);
|
|
10
|
+
}
|
|
11
|
+
case "object": return handlers.object(def);
|
|
12
|
+
case "array": return handlers.array(def);
|
|
13
|
+
case "union": return handlers.union(def);
|
|
14
|
+
case "intersection": return handlers.intersection(def);
|
|
15
|
+
case "tuple": return handlers.tuple(def);
|
|
16
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
3
21
|
//#region packages/typescript/src/validator.ts
|
|
4
22
|
function _define_property(obj, key, value) {
|
|
5
23
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -44,7 +62,17 @@ var Validator = class {
|
|
|
44
62
|
throw() {
|
|
45
63
|
throw new ValidatorError(this.errors);
|
|
46
64
|
}
|
|
47
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Validates a value against the type definition.
|
|
67
|
+
*
|
|
68
|
+
* Acts as a TypeScript type guard — when it returns `true`, the value
|
|
69
|
+
* is narrowed to `DataType`.
|
|
70
|
+
*
|
|
71
|
+
* @param value - The value to validate.
|
|
72
|
+
* @param safe - If `true`, returns `false` on failure instead of throwing.
|
|
73
|
+
* @returns `true` if the value matches the type definition.
|
|
74
|
+
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
75
|
+
*/ validate(value, safe) {
|
|
48
76
|
this.push("");
|
|
49
77
|
this.errors = [];
|
|
50
78
|
this.stackErrors = [];
|
|
@@ -71,15 +99,15 @@ var Validator = class {
|
|
|
71
99
|
return this.stackPath.slice(1).join(".");
|
|
72
100
|
}
|
|
73
101
|
validateAnnotatedType(def, value) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
102
|
+
return forAnnotatedType(def, {
|
|
103
|
+
final: (d) => this.validatePrimitive(d, value),
|
|
104
|
+
phantom: () => true,
|
|
105
|
+
object: (d) => this.validateObject(d, value),
|
|
106
|
+
array: (d) => this.validateArray(d, value),
|
|
107
|
+
union: (d) => this.validateUnion(d, value),
|
|
108
|
+
intersection: (d) => this.validateIntersection(d, value),
|
|
109
|
+
tuple: (d) => this.validateTuple(d, value)
|
|
110
|
+
});
|
|
83
111
|
}
|
|
84
112
|
validateUnion(def, value) {
|
|
85
113
|
let i = 0;
|
|
@@ -170,7 +198,7 @@ var Validator = class {
|
|
|
170
198
|
let partialFunctionMatched = false;
|
|
171
199
|
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.path);
|
|
172
200
|
for (const [key, item] of def.type.props.entries()) {
|
|
173
|
-
if (skipList.has(key)) continue;
|
|
201
|
+
if (skipList.has(key) || isPhantomType(item)) continue;
|
|
174
202
|
typeKeys.add(key);
|
|
175
203
|
if (value[key] === undefined) {
|
|
176
204
|
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
@@ -311,7 +339,7 @@ else {
|
|
|
311
339
|
constructor(def, opts) {
|
|
312
340
|
_define_property(this, "def", void 0);
|
|
313
341
|
_define_property(this, "opts", void 0);
|
|
314
|
-
_define_property(this, "errors", void 0);
|
|
342
|
+
/** Validation errors collected during the last {@link validate} call. */ _define_property(this, "errors", void 0);
|
|
315
343
|
_define_property(this, "stackErrors", void 0);
|
|
316
344
|
_define_property(this, "stackPath", void 0);
|
|
317
345
|
this.def = def;
|
|
@@ -452,6 +480,9 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
452
480
|
};
|
|
453
481
|
return handle;
|
|
454
482
|
}
|
|
483
|
+
function isPhantomType(def) {
|
|
484
|
+
return def.type.kind === "" && def.type.designType === "phantom";
|
|
485
|
+
}
|
|
455
486
|
function isAnnotatedTypeOfPrimitive(t) {
|
|
456
487
|
if (["array", "object"].includes(t.type.kind)) return false;
|
|
457
488
|
if (!t.type.kind) return true;
|
|
@@ -470,14 +501,16 @@ function isAnnotatedTypeOfPrimitive(t) {
|
|
|
470
501
|
//#region packages/typescript/src/json-schema.ts
|
|
471
502
|
function buildJsonSchema(type) {
|
|
472
503
|
const build = (def) => {
|
|
473
|
-
const t = def.type;
|
|
474
504
|
const meta = def.metadata;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
505
|
+
return forAnnotatedType(def, {
|
|
506
|
+
phantom() {
|
|
507
|
+
return {};
|
|
508
|
+
},
|
|
509
|
+
object(d) {
|
|
478
510
|
const properties = {};
|
|
479
511
|
const required = [];
|
|
480
|
-
for (const [key, val] of
|
|
512
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
513
|
+
if (isPhantomType(val)) continue;
|
|
481
514
|
properties[key] = build(val);
|
|
482
515
|
if (!val.optional) required.push(key);
|
|
483
516
|
}
|
|
@@ -487,41 +520,36 @@ function buildJsonSchema(type) {
|
|
|
487
520
|
};
|
|
488
521
|
if (required.length) schema.required = required;
|
|
489
522
|
return schema;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const arr = t;
|
|
523
|
+
},
|
|
524
|
+
array(d) {
|
|
493
525
|
const schema = {
|
|
494
526
|
type: "array",
|
|
495
|
-
items: build(
|
|
527
|
+
items: build(d.type.of)
|
|
496
528
|
};
|
|
497
529
|
const minLength = meta.get("expect.minLength");
|
|
498
530
|
if (typeof minLength === "number") schema.minItems = minLength;
|
|
499
531
|
const maxLength = meta.get("expect.maxLength");
|
|
500
532
|
if (typeof maxLength === "number") schema.maxItems = maxLength;
|
|
501
533
|
return schema;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
511
|
-
case "tuple": {
|
|
512
|
-
const grp = t;
|
|
534
|
+
},
|
|
535
|
+
union(d) {
|
|
536
|
+
return { anyOf: d.type.items.map(build) };
|
|
537
|
+
},
|
|
538
|
+
intersection(d) {
|
|
539
|
+
return { allOf: d.type.items.map(build) };
|
|
540
|
+
},
|
|
541
|
+
tuple(d) {
|
|
513
542
|
return {
|
|
514
543
|
type: "array",
|
|
515
|
-
items:
|
|
544
|
+
items: d.type.items.map(build),
|
|
516
545
|
additionalItems: false
|
|
517
546
|
};
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
const fin = t;
|
|
547
|
+
},
|
|
548
|
+
final(d) {
|
|
521
549
|
const schema = {};
|
|
522
|
-
if (
|
|
523
|
-
if (
|
|
524
|
-
schema.type =
|
|
550
|
+
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
551
|
+
if (d.type.designType && d.type.designType !== "any") {
|
|
552
|
+
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
525
553
|
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
526
554
|
}
|
|
527
555
|
if (schema.type === "string") {
|
|
@@ -541,17 +569,277 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
541
569
|
}
|
|
542
570
|
return schema;
|
|
543
571
|
}
|
|
544
|
-
|
|
545
|
-
}
|
|
572
|
+
});
|
|
546
573
|
};
|
|
547
574
|
return build(type);
|
|
548
575
|
}
|
|
576
|
+
function fromJsonSchema(schema) {
|
|
577
|
+
const convert = (s) => {
|
|
578
|
+
if (!s || Object.keys(s).length === 0) return defineAnnotatedType().designType("any").$type;
|
|
579
|
+
if (s.$ref) throw new Error("$ref is not supported by fromJsonSchema. Dereference the schema first.");
|
|
580
|
+
if ("const" in s) {
|
|
581
|
+
const val = s.const;
|
|
582
|
+
const dt = val === null ? "null" : typeof val;
|
|
583
|
+
return defineAnnotatedType().designType(dt).value(val).$type;
|
|
584
|
+
}
|
|
585
|
+
if (s.enum) {
|
|
586
|
+
const handle = defineAnnotatedType("union");
|
|
587
|
+
for (const val of s.enum) {
|
|
588
|
+
const dt = val === null ? "null" : typeof val;
|
|
589
|
+
handle.item(defineAnnotatedType().designType(dt).value(val).$type);
|
|
590
|
+
}
|
|
591
|
+
return handle.$type;
|
|
592
|
+
}
|
|
593
|
+
if (s.anyOf) {
|
|
594
|
+
const handle = defineAnnotatedType("union");
|
|
595
|
+
for (const item of s.anyOf) handle.item(convert(item));
|
|
596
|
+
return handle.$type;
|
|
597
|
+
}
|
|
598
|
+
if (s.oneOf) {
|
|
599
|
+
const handle = defineAnnotatedType("union");
|
|
600
|
+
for (const item of s.oneOf) handle.item(convert(item));
|
|
601
|
+
return handle.$type;
|
|
602
|
+
}
|
|
603
|
+
if (s.allOf && !s.type) {
|
|
604
|
+
const handle = defineAnnotatedType("intersection");
|
|
605
|
+
for (const item of s.allOf) handle.item(convert(item));
|
|
606
|
+
return handle.$type;
|
|
607
|
+
}
|
|
608
|
+
if (Array.isArray(s.type)) {
|
|
609
|
+
const handle = defineAnnotatedType("union");
|
|
610
|
+
for (const t of s.type) handle.item(convert({
|
|
611
|
+
...s,
|
|
612
|
+
type: t
|
|
613
|
+
}));
|
|
614
|
+
return handle.$type;
|
|
615
|
+
}
|
|
616
|
+
if (s.type === "object") {
|
|
617
|
+
const handle = defineAnnotatedType("object");
|
|
618
|
+
const required = new Set(s.required || []);
|
|
619
|
+
if (s.properties) for (const [key, propSchema] of Object.entries(s.properties)) {
|
|
620
|
+
const propType = convert(propSchema);
|
|
621
|
+
if (!required.has(key)) propType.optional = true;
|
|
622
|
+
handle.prop(key, propType);
|
|
623
|
+
}
|
|
624
|
+
return handle.$type;
|
|
625
|
+
}
|
|
626
|
+
if (s.type === "array") {
|
|
627
|
+
if (Array.isArray(s.items)) {
|
|
628
|
+
const handle$1 = defineAnnotatedType("tuple");
|
|
629
|
+
for (const item of s.items) handle$1.item(convert(item));
|
|
630
|
+
return handle$1.$type;
|
|
631
|
+
}
|
|
632
|
+
const itemType = s.items ? convert(s.items) : defineAnnotatedType().designType("any").$type;
|
|
633
|
+
const handle = defineAnnotatedType("array").of(itemType);
|
|
634
|
+
if (typeof s.minItems === "number") handle.annotate("expect.minLength", s.minItems);
|
|
635
|
+
if (typeof s.maxItems === "number") handle.annotate("expect.maxLength", s.maxItems);
|
|
636
|
+
return handle.$type;
|
|
637
|
+
}
|
|
638
|
+
if (s.type === "string") {
|
|
639
|
+
const handle = defineAnnotatedType().designType("string").tags("string");
|
|
640
|
+
if (typeof s.minLength === "number") handle.annotate("expect.minLength", s.minLength);
|
|
641
|
+
if (typeof s.maxLength === "number") handle.annotate("expect.maxLength", s.maxLength);
|
|
642
|
+
if (s.pattern) handle.annotate("expect.pattern", { pattern: s.pattern }, true);
|
|
643
|
+
if (s.allOf) {
|
|
644
|
+
for (const item of s.allOf) if (item.pattern) handle.annotate("expect.pattern", { pattern: item.pattern }, true);
|
|
645
|
+
}
|
|
646
|
+
return handle.$type;
|
|
647
|
+
}
|
|
648
|
+
if (s.type === "integer") {
|
|
649
|
+
const handle = defineAnnotatedType().designType("number").tags("number");
|
|
650
|
+
handle.annotate("expect.int", true);
|
|
651
|
+
if (typeof s.minimum === "number") handle.annotate("expect.min", s.minimum);
|
|
652
|
+
if (typeof s.maximum === "number") handle.annotate("expect.max", s.maximum);
|
|
653
|
+
return handle.$type;
|
|
654
|
+
}
|
|
655
|
+
if (s.type === "number") {
|
|
656
|
+
const handle = defineAnnotatedType().designType("number").tags("number");
|
|
657
|
+
if (typeof s.minimum === "number") handle.annotate("expect.min", s.minimum);
|
|
658
|
+
if (typeof s.maximum === "number") handle.annotate("expect.max", s.maximum);
|
|
659
|
+
return handle.$type;
|
|
660
|
+
}
|
|
661
|
+
if (s.type === "boolean") return defineAnnotatedType().designType("boolean").tags("boolean").$type;
|
|
662
|
+
if (s.type === "null") return defineAnnotatedType().designType("null").tags("null").$type;
|
|
663
|
+
return defineAnnotatedType().designType("any").$type;
|
|
664
|
+
};
|
|
665
|
+
return convert(schema);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
//#endregion
|
|
669
|
+
//#region packages/typescript/src/serialize.ts
|
|
670
|
+
const SERIALIZE_VERSION = 1;
|
|
671
|
+
function serializeAnnotatedType(type, options) {
|
|
672
|
+
const result = serializeNode(type, [], options);
|
|
673
|
+
result.$v = SERIALIZE_VERSION;
|
|
674
|
+
return result;
|
|
675
|
+
}
|
|
676
|
+
function serializeNode(def, path, options) {
|
|
677
|
+
const result = {
|
|
678
|
+
type: serializeTypeDef(def, path, options),
|
|
679
|
+
metadata: serializeMetadata(def.metadata, path, def.type.kind, options)
|
|
680
|
+
};
|
|
681
|
+
if (def.optional) result.optional = true;
|
|
682
|
+
return result;
|
|
683
|
+
}
|
|
684
|
+
function serializeTypeDef(def, path, options) {
|
|
685
|
+
return forAnnotatedType(def, {
|
|
686
|
+
phantom(d) {
|
|
687
|
+
return {
|
|
688
|
+
kind: "",
|
|
689
|
+
designType: d.type.designType,
|
|
690
|
+
tags: Array.from(d.type.tags)
|
|
691
|
+
};
|
|
692
|
+
},
|
|
693
|
+
final(d) {
|
|
694
|
+
const result = {
|
|
695
|
+
kind: "",
|
|
696
|
+
designType: d.type.designType,
|
|
697
|
+
tags: Array.from(d.type.tags)
|
|
698
|
+
};
|
|
699
|
+
if (d.type.value !== undefined) result.value = d.type.value;
|
|
700
|
+
return result;
|
|
701
|
+
},
|
|
702
|
+
object(d) {
|
|
703
|
+
const props = {};
|
|
704
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
705
|
+
if (isPhantomType(val)) continue;
|
|
706
|
+
props[key] = serializeNode(val, [...path, key], options);
|
|
707
|
+
}
|
|
708
|
+
const propsPatterns = d.type.propsPatterns.map((pp) => ({
|
|
709
|
+
pattern: {
|
|
710
|
+
source: pp.pattern.source,
|
|
711
|
+
flags: pp.pattern.flags
|
|
712
|
+
},
|
|
713
|
+
def: serializeNode(pp.def, path, options)
|
|
714
|
+
}));
|
|
715
|
+
return {
|
|
716
|
+
kind: "object",
|
|
717
|
+
props,
|
|
718
|
+
propsPatterns,
|
|
719
|
+
tags: Array.from(d.type.tags)
|
|
720
|
+
};
|
|
721
|
+
},
|
|
722
|
+
array(d) {
|
|
723
|
+
return {
|
|
724
|
+
kind: "array",
|
|
725
|
+
of: serializeNode(d.type.of, path, options),
|
|
726
|
+
tags: Array.from(d.type.tags)
|
|
727
|
+
};
|
|
728
|
+
},
|
|
729
|
+
union(d) {
|
|
730
|
+
return {
|
|
731
|
+
kind: "union",
|
|
732
|
+
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
733
|
+
tags: Array.from(d.type.tags)
|
|
734
|
+
};
|
|
735
|
+
},
|
|
736
|
+
intersection(d) {
|
|
737
|
+
return {
|
|
738
|
+
kind: "intersection",
|
|
739
|
+
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
740
|
+
tags: Array.from(d.type.tags)
|
|
741
|
+
};
|
|
742
|
+
},
|
|
743
|
+
tuple(d) {
|
|
744
|
+
return {
|
|
745
|
+
kind: "tuple",
|
|
746
|
+
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
747
|
+
tags: Array.from(d.type.tags)
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
function serializeMetadata(metadata, path, kind, options) {
|
|
753
|
+
const result = {};
|
|
754
|
+
const ignoreSet = options?.ignoreAnnotations ? new Set(options.ignoreAnnotations) : undefined;
|
|
755
|
+
for (const [key, value] of metadata.entries()) {
|
|
756
|
+
if (ignoreSet?.has(key)) continue;
|
|
757
|
+
if (options?.processAnnotation) {
|
|
758
|
+
const processed = options.processAnnotation({
|
|
759
|
+
key,
|
|
760
|
+
value,
|
|
761
|
+
path,
|
|
762
|
+
kind
|
|
763
|
+
});
|
|
764
|
+
if (processed === undefined || processed === null) continue;
|
|
765
|
+
result[processed.key] = processed.value;
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
result[key] = value;
|
|
769
|
+
}
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
772
|
+
function deserializeAnnotatedType(data) {
|
|
773
|
+
if (data.$v !== SERIALIZE_VERSION) throw new Error(`Unsupported serialized type version: ${data.$v} (expected ${SERIALIZE_VERSION})`);
|
|
774
|
+
return deserializeNode(data);
|
|
775
|
+
}
|
|
776
|
+
function deserializeNode(data) {
|
|
777
|
+
const metadata = new Map(Object.entries(data.metadata));
|
|
778
|
+
const type = deserializeTypeDef(data.type);
|
|
779
|
+
const result = {
|
|
780
|
+
__is_atscript_annotated_type: true,
|
|
781
|
+
type,
|
|
782
|
+
metadata,
|
|
783
|
+
validator(opts) {
|
|
784
|
+
return new Validator(this, opts);
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
if (data.optional) result.optional = true;
|
|
788
|
+
return result;
|
|
789
|
+
}
|
|
790
|
+
function deserializeTypeDef(t) {
|
|
791
|
+
const tags = new Set(t.tags);
|
|
792
|
+
switch (t.kind) {
|
|
793
|
+
case "": {
|
|
794
|
+
const result = {
|
|
795
|
+
kind: "",
|
|
796
|
+
designType: t.designType,
|
|
797
|
+
tags
|
|
798
|
+
};
|
|
799
|
+
if (t.value !== undefined) result.value = t.value;
|
|
800
|
+
return result;
|
|
801
|
+
}
|
|
802
|
+
case "object": {
|
|
803
|
+
const props = new Map();
|
|
804
|
+
for (const [key, val] of Object.entries(t.props)) props.set(key, deserializeNode(val));
|
|
805
|
+
const propsPatterns = t.propsPatterns.map((pp) => ({
|
|
806
|
+
pattern: new RegExp(pp.pattern.source, pp.pattern.flags),
|
|
807
|
+
def: deserializeNode(pp.def)
|
|
808
|
+
}));
|
|
809
|
+
return {
|
|
810
|
+
kind: "object",
|
|
811
|
+
props,
|
|
812
|
+
propsPatterns,
|
|
813
|
+
tags
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
case "array": return {
|
|
817
|
+
kind: "array",
|
|
818
|
+
of: deserializeNode(t.of),
|
|
819
|
+
tags
|
|
820
|
+
};
|
|
821
|
+
case "union":
|
|
822
|
+
case "intersection":
|
|
823
|
+
case "tuple": return {
|
|
824
|
+
kind: t.kind,
|
|
825
|
+
items: t.items.map((item) => deserializeNode(item)),
|
|
826
|
+
tags
|
|
827
|
+
};
|
|
828
|
+
default: throw new Error(`Unknown serialized type kind "${t.kind}"`);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
549
831
|
|
|
550
832
|
//#endregion
|
|
833
|
+
exports.SERIALIZE_VERSION = SERIALIZE_VERSION
|
|
551
834
|
exports.Validator = Validator
|
|
552
835
|
exports.ValidatorError = ValidatorError
|
|
553
836
|
exports.annotate = annotate
|
|
554
837
|
exports.buildJsonSchema = buildJsonSchema
|
|
555
838
|
exports.defineAnnotatedType = defineAnnotatedType
|
|
839
|
+
exports.deserializeAnnotatedType = deserializeAnnotatedType
|
|
840
|
+
exports.forAnnotatedType = forAnnotatedType
|
|
841
|
+
exports.fromJsonSchema = fromJsonSchema
|
|
556
842
|
exports.isAnnotatedType = isAnnotatedType
|
|
557
|
-
exports.isAnnotatedTypeOfPrimitive = isAnnotatedTypeOfPrimitive
|
|
843
|
+
exports.isAnnotatedTypeOfPrimitive = isAnnotatedTypeOfPrimitive
|
|
844
|
+
exports.isPhantomType = isPhantomType
|
|
845
|
+
exports.serializeAnnotatedType = serializeAnnotatedType
|