@atscript/typescript 0.1.2 → 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/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
- validate(value, safe) {
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
- switch (def.type.kind) {
74
- case "object": return this.validateObject(def, value);
75
- case "union": return this.validateUnion(def, value);
76
- case "intersection": return this.validateIntersection(def, value);
77
- case "tuple": return this.validateTuple(def, value);
78
- case "array": return this.validateArray(def, value);
79
- case "": return this.validatePrimitive(def, value);
80
- default: throw new Error(`Unknown type "${def.type.kind}"`);
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;
@@ -469,14 +492,12 @@ function isAnnotatedTypeOfPrimitive(t) {
469
492
  //#region packages/typescript/src/json-schema.ts
470
493
  function buildJsonSchema(type) {
471
494
  const build = (def) => {
472
- const t = def.type;
473
495
  const meta = def.metadata;
474
- switch (t.kind) {
475
- case "object": {
476
- const obj = t;
496
+ return forAnnotatedType(def, {
497
+ object(d) {
477
498
  const properties = {};
478
499
  const required = [];
479
- for (const [key, val] of obj.props.entries()) {
500
+ for (const [key, val] of d.type.props.entries()) {
480
501
  properties[key] = build(val);
481
502
  if (!val.optional) required.push(key);
482
503
  }
@@ -486,41 +507,36 @@ function buildJsonSchema(type) {
486
507
  };
487
508
  if (required.length) schema.required = required;
488
509
  return schema;
489
- }
490
- case "array": {
491
- const arr = t;
510
+ },
511
+ array(d) {
492
512
  const schema = {
493
513
  type: "array",
494
- items: build(arr.of)
514
+ items: build(d.type.of)
495
515
  };
496
516
  const minLength = meta.get("expect.minLength");
497
517
  if (typeof minLength === "number") schema.minItems = minLength;
498
518
  const maxLength = meta.get("expect.maxLength");
499
519
  if (typeof maxLength === "number") schema.maxItems = maxLength;
500
520
  return schema;
501
- }
502
- case "union": {
503
- const grp = t;
504
- return { anyOf: grp.items.map(build) };
505
- }
506
- case "intersection": {
507
- const grp = t;
508
- return { allOf: grp.items.map(build) };
509
- }
510
- case "tuple": {
511
- 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) {
512
529
  return {
513
530
  type: "array",
514
- items: grp.items.map(build),
531
+ items: d.type.items.map(build),
515
532
  additionalItems: false
516
533
  };
517
- }
518
- case "": {
519
- const fin = t;
534
+ },
535
+ final(d) {
520
536
  const schema = {};
521
- if (fin.value !== undefined) schema.const = fin.value;
522
- if (fin.designType && fin.designType !== "any") {
523
- schema.type = fin.designType === "undefined" ? "null" : fin.designType;
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;
524
540
  if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
525
541
  }
526
542
  if (schema.type === "string") {
@@ -540,11 +556,255 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
540
556
  }
541
557
  return schema;
542
558
  }
543
- default: return {};
544
- }
559
+ });
545
560
  };
546
561
  return build(type);
547
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
+ }
548
808
 
549
809
  //#endregion
550
- export { Validator, ValidatorError, annotate, 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.2",
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.2"
73
+ "@atscript/core": "^0.1.3"
74
74
  },
75
75
  "dependencies": {
76
76
  "@moostjs/event-cli": "^0.5.32",