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