@atscript/typescript 0.1.1 → 0.1.3
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 +252 -133
- package/dist/index.cjs +252 -133
- package/dist/index.d.ts +8 -2
- package/dist/index.mjs +252 -133
- package/dist/utils.cjs +317 -47
- package/dist/utils.d.ts +280 -12
- package/dist/utils.mjs +311 -47
- package/package.json +2 -2
package/dist/utils.mjs
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
|
|
2
|
+
//#region packages/typescript/src/traverse.ts
|
|
3
|
+
function forAnnotatedType(def, handlers) {
|
|
4
|
+
switch (def.type.kind) {
|
|
5
|
+
case "": return handlers.final(def);
|
|
6
|
+
case "object": return handlers.object(def);
|
|
7
|
+
case "array": return handlers.array(def);
|
|
8
|
+
case "union": return handlers.union(def);
|
|
9
|
+
case "intersection": return handlers.intersection(def);
|
|
10
|
+
case "tuple": return handlers.tuple(def);
|
|
11
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
2
16
|
//#region packages/typescript/src/validator.ts
|
|
3
17
|
function _define_property(obj, key, value) {
|
|
4
18
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -43,7 +57,17 @@ var Validator = class {
|
|
|
43
57
|
throw() {
|
|
44
58
|
throw new ValidatorError(this.errors);
|
|
45
59
|
}
|
|
46
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Validates a value against the type definition.
|
|
62
|
+
*
|
|
63
|
+
* Acts as a TypeScript type guard — when it returns `true`, the value
|
|
64
|
+
* is narrowed to `DataType`.
|
|
65
|
+
*
|
|
66
|
+
* @param value - The value to validate.
|
|
67
|
+
* @param safe - If `true`, returns `false` on failure instead of throwing.
|
|
68
|
+
* @returns `true` if the value matches the type definition.
|
|
69
|
+
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
70
|
+
*/ validate(value, safe) {
|
|
47
71
|
this.push("");
|
|
48
72
|
this.errors = [];
|
|
49
73
|
this.stackErrors = [];
|
|
@@ -70,15 +94,14 @@ var Validator = class {
|
|
|
70
94
|
return this.stackPath.slice(1).join(".");
|
|
71
95
|
}
|
|
72
96
|
validateAnnotatedType(def, value) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
97
|
+
return forAnnotatedType(def, {
|
|
98
|
+
final: (d) => this.validatePrimitive(d, value),
|
|
99
|
+
object: (d) => this.validateObject(d, value),
|
|
100
|
+
array: (d) => this.validateArray(d, value),
|
|
101
|
+
union: (d) => this.validateUnion(d, value),
|
|
102
|
+
intersection: (d) => this.validateIntersection(d, value),
|
|
103
|
+
tuple: (d) => this.validateTuple(d, value)
|
|
104
|
+
});
|
|
82
105
|
}
|
|
83
106
|
validateUnion(def, value) {
|
|
84
107
|
let i = 0;
|
|
@@ -310,7 +333,7 @@ else {
|
|
|
310
333
|
constructor(def, opts) {
|
|
311
334
|
_define_property(this, "def", void 0);
|
|
312
335
|
_define_property(this, "opts", void 0);
|
|
313
|
-
_define_property(this, "errors", void 0);
|
|
336
|
+
/** Validation errors collected during the last {@link validate} call. */ _define_property(this, "errors", void 0);
|
|
314
337
|
_define_property(this, "stackErrors", void 0);
|
|
315
338
|
_define_property(this, "stackPath", void 0);
|
|
316
339
|
this.def = def;
|
|
@@ -337,6 +360,15 @@ var ValidatorError = class extends Error {
|
|
|
337
360
|
function isAnnotatedType(type) {
|
|
338
361
|
return type && type.__is_atscript_annotated_type;
|
|
339
362
|
}
|
|
363
|
+
function annotate(metadata, key, value, asArray) {
|
|
364
|
+
if (!metadata) return;
|
|
365
|
+
if (asArray) if (metadata.has(key)) {
|
|
366
|
+
const a = metadata.get(key);
|
|
367
|
+
if (Array.isArray(a)) a.push(value);
|
|
368
|
+
else metadata.set(key, [a, value]);
|
|
369
|
+
} else metadata.set(key, [value]);
|
|
370
|
+
else metadata.set(key, value);
|
|
371
|
+
}
|
|
340
372
|
function defineAnnotatedType(_kind, base) {
|
|
341
373
|
const kind = _kind || "";
|
|
342
374
|
const type = base?.type || {};
|
|
@@ -436,12 +468,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
436
468
|
return this;
|
|
437
469
|
},
|
|
438
470
|
annotate(key, value, asArray) {
|
|
439
|
-
|
|
440
|
-
const a = this.$metadata.get(key);
|
|
441
|
-
if (Array.isArray(a)) a.push(value);
|
|
442
|
-
else this.$metadata.set(key, [a, value]);
|
|
443
|
-
} else this.$metadata.set(key, [value]);
|
|
444
|
-
else this.$metadata.set(key, value);
|
|
471
|
+
annotate(this.$metadata, key, value, asArray);
|
|
445
472
|
return this;
|
|
446
473
|
}
|
|
447
474
|
};
|
|
@@ -465,14 +492,12 @@ function isAnnotatedTypeOfPrimitive(t) {
|
|
|
465
492
|
//#region packages/typescript/src/json-schema.ts
|
|
466
493
|
function buildJsonSchema(type) {
|
|
467
494
|
const build = (def) => {
|
|
468
|
-
const t = def.type;
|
|
469
495
|
const meta = def.metadata;
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const obj = t;
|
|
496
|
+
return forAnnotatedType(def, {
|
|
497
|
+
object(d) {
|
|
473
498
|
const properties = {};
|
|
474
499
|
const required = [];
|
|
475
|
-
for (const [key, val] of
|
|
500
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
476
501
|
properties[key] = build(val);
|
|
477
502
|
if (!val.optional) required.push(key);
|
|
478
503
|
}
|
|
@@ -482,41 +507,36 @@ function buildJsonSchema(type) {
|
|
|
482
507
|
};
|
|
483
508
|
if (required.length) schema.required = required;
|
|
484
509
|
return schema;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
const arr = t;
|
|
510
|
+
},
|
|
511
|
+
array(d) {
|
|
488
512
|
const schema = {
|
|
489
513
|
type: "array",
|
|
490
|
-
items: build(
|
|
514
|
+
items: build(d.type.of)
|
|
491
515
|
};
|
|
492
516
|
const minLength = meta.get("expect.minLength");
|
|
493
517
|
if (typeof minLength === "number") schema.minItems = minLength;
|
|
494
518
|
const maxLength = meta.get("expect.maxLength");
|
|
495
519
|
if (typeof maxLength === "number") schema.maxItems = maxLength;
|
|
496
520
|
return schema;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
case "tuple": {
|
|
507
|
-
const grp = t;
|
|
521
|
+
},
|
|
522
|
+
union(d) {
|
|
523
|
+
return { anyOf: d.type.items.map(build) };
|
|
524
|
+
},
|
|
525
|
+
intersection(d) {
|
|
526
|
+
return { allOf: d.type.items.map(build) };
|
|
527
|
+
},
|
|
528
|
+
tuple(d) {
|
|
508
529
|
return {
|
|
509
530
|
type: "array",
|
|
510
|
-
items:
|
|
531
|
+
items: d.type.items.map(build),
|
|
511
532
|
additionalItems: false
|
|
512
533
|
};
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
const fin = t;
|
|
534
|
+
},
|
|
535
|
+
final(d) {
|
|
516
536
|
const schema = {};
|
|
517
|
-
if (
|
|
518
|
-
if (
|
|
519
|
-
schema.type =
|
|
537
|
+
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
538
|
+
if (d.type.designType && d.type.designType !== "any") {
|
|
539
|
+
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
520
540
|
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
521
541
|
}
|
|
522
542
|
if (schema.type === "string") {
|
|
@@ -536,11 +556,255 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
536
556
|
}
|
|
537
557
|
return schema;
|
|
538
558
|
}
|
|
539
|
-
|
|
540
|
-
}
|
|
559
|
+
});
|
|
541
560
|
};
|
|
542
561
|
return build(type);
|
|
543
562
|
}
|
|
563
|
+
function fromJsonSchema(schema) {
|
|
564
|
+
const convert = (s) => {
|
|
565
|
+
if (!s || Object.keys(s).length === 0) return defineAnnotatedType().designType("any").$type;
|
|
566
|
+
if (s.$ref) throw new Error("$ref is not supported by fromJsonSchema. Dereference the schema first.");
|
|
567
|
+
if ("const" in s) {
|
|
568
|
+
const val = s.const;
|
|
569
|
+
const dt = val === null ? "null" : typeof val;
|
|
570
|
+
return defineAnnotatedType().designType(dt).value(val).$type;
|
|
571
|
+
}
|
|
572
|
+
if (s.enum) {
|
|
573
|
+
const handle = defineAnnotatedType("union");
|
|
574
|
+
for (const val of s.enum) {
|
|
575
|
+
const dt = val === null ? "null" : typeof val;
|
|
576
|
+
handle.item(defineAnnotatedType().designType(dt).value(val).$type);
|
|
577
|
+
}
|
|
578
|
+
return handle.$type;
|
|
579
|
+
}
|
|
580
|
+
if (s.anyOf) {
|
|
581
|
+
const handle = defineAnnotatedType("union");
|
|
582
|
+
for (const item of s.anyOf) handle.item(convert(item));
|
|
583
|
+
return handle.$type;
|
|
584
|
+
}
|
|
585
|
+
if (s.oneOf) {
|
|
586
|
+
const handle = defineAnnotatedType("union");
|
|
587
|
+
for (const item of s.oneOf) handle.item(convert(item));
|
|
588
|
+
return handle.$type;
|
|
589
|
+
}
|
|
590
|
+
if (s.allOf && !s.type) {
|
|
591
|
+
const handle = defineAnnotatedType("intersection");
|
|
592
|
+
for (const item of s.allOf) handle.item(convert(item));
|
|
593
|
+
return handle.$type;
|
|
594
|
+
}
|
|
595
|
+
if (Array.isArray(s.type)) {
|
|
596
|
+
const handle = defineAnnotatedType("union");
|
|
597
|
+
for (const t of s.type) handle.item(convert({
|
|
598
|
+
...s,
|
|
599
|
+
type: t
|
|
600
|
+
}));
|
|
601
|
+
return handle.$type;
|
|
602
|
+
}
|
|
603
|
+
if (s.type === "object") {
|
|
604
|
+
const handle = defineAnnotatedType("object");
|
|
605
|
+
const required = new Set(s.required || []);
|
|
606
|
+
if (s.properties) for (const [key, propSchema] of Object.entries(s.properties)) {
|
|
607
|
+
const propType = convert(propSchema);
|
|
608
|
+
if (!required.has(key)) propType.optional = true;
|
|
609
|
+
handle.prop(key, propType);
|
|
610
|
+
}
|
|
611
|
+
return handle.$type;
|
|
612
|
+
}
|
|
613
|
+
if (s.type === "array") {
|
|
614
|
+
if (Array.isArray(s.items)) {
|
|
615
|
+
const handle$1 = defineAnnotatedType("tuple");
|
|
616
|
+
for (const item of s.items) handle$1.item(convert(item));
|
|
617
|
+
return handle$1.$type;
|
|
618
|
+
}
|
|
619
|
+
const itemType = s.items ? convert(s.items) : defineAnnotatedType().designType("any").$type;
|
|
620
|
+
const handle = defineAnnotatedType("array").of(itemType);
|
|
621
|
+
if (typeof s.minItems === "number") handle.annotate("expect.minLength", s.minItems);
|
|
622
|
+
if (typeof s.maxItems === "number") handle.annotate("expect.maxLength", s.maxItems);
|
|
623
|
+
return handle.$type;
|
|
624
|
+
}
|
|
625
|
+
if (s.type === "string") {
|
|
626
|
+
const handle = defineAnnotatedType().designType("string").tags("string");
|
|
627
|
+
if (typeof s.minLength === "number") handle.annotate("expect.minLength", s.minLength);
|
|
628
|
+
if (typeof s.maxLength === "number") handle.annotate("expect.maxLength", s.maxLength);
|
|
629
|
+
if (s.pattern) handle.annotate("expect.pattern", { pattern: s.pattern }, true);
|
|
630
|
+
if (s.allOf) {
|
|
631
|
+
for (const item of s.allOf) if (item.pattern) handle.annotate("expect.pattern", { pattern: item.pattern }, true);
|
|
632
|
+
}
|
|
633
|
+
return handle.$type;
|
|
634
|
+
}
|
|
635
|
+
if (s.type === "integer") {
|
|
636
|
+
const handle = defineAnnotatedType().designType("number").tags("number");
|
|
637
|
+
handle.annotate("expect.int", true);
|
|
638
|
+
if (typeof s.minimum === "number") handle.annotate("expect.min", s.minimum);
|
|
639
|
+
if (typeof s.maximum === "number") handle.annotate("expect.max", s.maximum);
|
|
640
|
+
return handle.$type;
|
|
641
|
+
}
|
|
642
|
+
if (s.type === "number") {
|
|
643
|
+
const handle = defineAnnotatedType().designType("number").tags("number");
|
|
644
|
+
if (typeof s.minimum === "number") handle.annotate("expect.min", s.minimum);
|
|
645
|
+
if (typeof s.maximum === "number") handle.annotate("expect.max", s.maximum);
|
|
646
|
+
return handle.$type;
|
|
647
|
+
}
|
|
648
|
+
if (s.type === "boolean") return defineAnnotatedType().designType("boolean").tags("boolean").$type;
|
|
649
|
+
if (s.type === "null") return defineAnnotatedType().designType("null").tags("null").$type;
|
|
650
|
+
return defineAnnotatedType().designType("any").$type;
|
|
651
|
+
};
|
|
652
|
+
return convert(schema);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
//#endregion
|
|
656
|
+
//#region packages/typescript/src/serialize.ts
|
|
657
|
+
const SERIALIZE_VERSION = 1;
|
|
658
|
+
function serializeAnnotatedType(type, options) {
|
|
659
|
+
const result = serializeNode(type, [], options);
|
|
660
|
+
result.$v = SERIALIZE_VERSION;
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
function serializeNode(def, path, options) {
|
|
664
|
+
const result = {
|
|
665
|
+
type: serializeTypeDef(def, path, options),
|
|
666
|
+
metadata: serializeMetadata(def.metadata, path, def.type.kind, options)
|
|
667
|
+
};
|
|
668
|
+
if (def.optional) result.optional = true;
|
|
669
|
+
return result;
|
|
670
|
+
}
|
|
671
|
+
function serializeTypeDef(def, path, options) {
|
|
672
|
+
return forAnnotatedType(def, {
|
|
673
|
+
final(d) {
|
|
674
|
+
const result = {
|
|
675
|
+
kind: "",
|
|
676
|
+
designType: d.type.designType,
|
|
677
|
+
tags: Array.from(d.type.tags)
|
|
678
|
+
};
|
|
679
|
+
if (d.type.value !== undefined) result.value = d.type.value;
|
|
680
|
+
return result;
|
|
681
|
+
},
|
|
682
|
+
object(d) {
|
|
683
|
+
const props = {};
|
|
684
|
+
for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options);
|
|
685
|
+
const propsPatterns = d.type.propsPatterns.map((pp) => ({
|
|
686
|
+
pattern: {
|
|
687
|
+
source: pp.pattern.source,
|
|
688
|
+
flags: pp.pattern.flags
|
|
689
|
+
},
|
|
690
|
+
def: serializeNode(pp.def, path, options)
|
|
691
|
+
}));
|
|
692
|
+
return {
|
|
693
|
+
kind: "object",
|
|
694
|
+
props,
|
|
695
|
+
propsPatterns,
|
|
696
|
+
tags: Array.from(d.type.tags)
|
|
697
|
+
};
|
|
698
|
+
},
|
|
699
|
+
array(d) {
|
|
700
|
+
return {
|
|
701
|
+
kind: "array",
|
|
702
|
+
of: serializeNode(d.type.of, path, options),
|
|
703
|
+
tags: Array.from(d.type.tags)
|
|
704
|
+
};
|
|
705
|
+
},
|
|
706
|
+
union(d) {
|
|
707
|
+
return {
|
|
708
|
+
kind: "union",
|
|
709
|
+
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
710
|
+
tags: Array.from(d.type.tags)
|
|
711
|
+
};
|
|
712
|
+
},
|
|
713
|
+
intersection(d) {
|
|
714
|
+
return {
|
|
715
|
+
kind: "intersection",
|
|
716
|
+
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
717
|
+
tags: Array.from(d.type.tags)
|
|
718
|
+
};
|
|
719
|
+
},
|
|
720
|
+
tuple(d) {
|
|
721
|
+
return {
|
|
722
|
+
kind: "tuple",
|
|
723
|
+
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
724
|
+
tags: Array.from(d.type.tags)
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
function serializeMetadata(metadata, path, kind, options) {
|
|
730
|
+
const result = {};
|
|
731
|
+
const ignoreSet = options?.ignoreAnnotations ? new Set(options.ignoreAnnotations) : undefined;
|
|
732
|
+
for (const [key, value] of metadata.entries()) {
|
|
733
|
+
if (ignoreSet?.has(key)) continue;
|
|
734
|
+
if (options?.processAnnotation) {
|
|
735
|
+
const processed = options.processAnnotation({
|
|
736
|
+
key,
|
|
737
|
+
value,
|
|
738
|
+
path,
|
|
739
|
+
kind
|
|
740
|
+
});
|
|
741
|
+
if (processed === undefined || processed === null) continue;
|
|
742
|
+
result[processed.key] = processed.value;
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
result[key] = value;
|
|
746
|
+
}
|
|
747
|
+
return result;
|
|
748
|
+
}
|
|
749
|
+
function deserializeAnnotatedType(data) {
|
|
750
|
+
if (data.$v !== SERIALIZE_VERSION) throw new Error(`Unsupported serialized type version: ${data.$v} (expected ${SERIALIZE_VERSION})`);
|
|
751
|
+
return deserializeNode(data);
|
|
752
|
+
}
|
|
753
|
+
function deserializeNode(data) {
|
|
754
|
+
const metadata = new Map(Object.entries(data.metadata));
|
|
755
|
+
const type = deserializeTypeDef(data.type);
|
|
756
|
+
const result = {
|
|
757
|
+
__is_atscript_annotated_type: true,
|
|
758
|
+
type,
|
|
759
|
+
metadata,
|
|
760
|
+
validator(opts) {
|
|
761
|
+
return new Validator(this, opts);
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
if (data.optional) result.optional = true;
|
|
765
|
+
return result;
|
|
766
|
+
}
|
|
767
|
+
function deserializeTypeDef(t) {
|
|
768
|
+
const tags = new Set(t.tags);
|
|
769
|
+
switch (t.kind) {
|
|
770
|
+
case "": {
|
|
771
|
+
const result = {
|
|
772
|
+
kind: "",
|
|
773
|
+
designType: t.designType,
|
|
774
|
+
tags
|
|
775
|
+
};
|
|
776
|
+
if (t.value !== undefined) result.value = t.value;
|
|
777
|
+
return result;
|
|
778
|
+
}
|
|
779
|
+
case "object": {
|
|
780
|
+
const props = new Map();
|
|
781
|
+
for (const [key, val] of Object.entries(t.props)) props.set(key, deserializeNode(val));
|
|
782
|
+
const propsPatterns = t.propsPatterns.map((pp) => ({
|
|
783
|
+
pattern: new RegExp(pp.pattern.source, pp.pattern.flags),
|
|
784
|
+
def: deserializeNode(pp.def)
|
|
785
|
+
}));
|
|
786
|
+
return {
|
|
787
|
+
kind: "object",
|
|
788
|
+
props,
|
|
789
|
+
propsPatterns,
|
|
790
|
+
tags
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
case "array": return {
|
|
794
|
+
kind: "array",
|
|
795
|
+
of: deserializeNode(t.of),
|
|
796
|
+
tags
|
|
797
|
+
};
|
|
798
|
+
case "union":
|
|
799
|
+
case "intersection":
|
|
800
|
+
case "tuple": return {
|
|
801
|
+
kind: t.kind,
|
|
802
|
+
items: t.items.map((item) => deserializeNode(item)),
|
|
803
|
+
tags
|
|
804
|
+
};
|
|
805
|
+
default: throw new Error(`Unknown serialized type kind "${t.kind}"`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
544
808
|
|
|
545
809
|
//#endregion
|
|
546
|
-
export { Validator, ValidatorError, buildJsonSchema, defineAnnotatedType, isAnnotatedType, isAnnotatedTypeOfPrimitive };
|
|
810
|
+
export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, defineAnnotatedType, deserializeAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, serializeAnnotatedType };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/typescript",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Atscript: typescript-gen support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"homepage": "https://github.com/moostjs/atscript/tree/main/packages/typescript#readme",
|
|
71
71
|
"license": "ISC",
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@atscript/core": "^0.1.
|
|
73
|
+
"@atscript/core": "^0.1.3"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@moostjs/event-cli": "^0.5.32",
|