@atscript/typescript 0.1.24 → 0.1.25

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 CHANGED
@@ -991,6 +991,46 @@ function isPhantomType(def) {
991
991
 
992
992
  //#endregion
993
993
  //#region packages/typescript/src/json-schema.ts
994
+ /**
995
+ * Detects a discriminator property across union items.
996
+ *
997
+ * Scans all items for object-typed members that share a common property
998
+ * with distinct const/literal values. If exactly one such property exists,
999
+ * it is returned as the discriminator.
1000
+ */ function detectDiscriminator(items) {
1001
+ if (items.length < 2) return null;
1002
+ for (const item of items) if (item.type.kind !== "object") return null;
1003
+ const firstObj = items[0].type;
1004
+ const candidates = [];
1005
+ for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
1006
+ const validCandidates = [];
1007
+ for (const candidate of candidates) {
1008
+ const values = new Set();
1009
+ const mapping = {};
1010
+ let valid = true;
1011
+ for (let i = 0; i < items.length; i++) {
1012
+ const obj = items[i].type;
1013
+ const prop = obj.props.get(candidate);
1014
+ if (!prop || prop.type.kind !== "" || prop.type.value === undefined) {
1015
+ valid = false;
1016
+ break;
1017
+ }
1018
+ const val = prop.type.value;
1019
+ if (values.has(val)) {
1020
+ valid = false;
1021
+ break;
1022
+ }
1023
+ values.add(val);
1024
+ mapping[String(val)] = `#/oneOf/${i}`;
1025
+ }
1026
+ if (valid) validCandidates.push({
1027
+ propertyName: candidate,
1028
+ mapping
1029
+ });
1030
+ }
1031
+ if (validCandidates.length === 1) return validCandidates[0];
1032
+ return null;
1033
+ }
994
1034
  function buildJsonSchema(type) {
995
1035
  const build$1 = (def) => {
996
1036
  const meta = def.metadata;
@@ -1025,6 +1065,14 @@ function buildJsonSchema(type) {
1025
1065
  return schema;
1026
1066
  },
1027
1067
  union(d) {
1068
+ const disc = detectDiscriminator(d.type.items);
1069
+ if (disc) return {
1070
+ oneOf: d.type.items.map(build$1),
1071
+ discriminator: {
1072
+ propertyName: disc.propertyName,
1073
+ mapping: disc.mapping
1074
+ }
1075
+ };
1028
1076
  return { anyOf: d.type.items.map(build$1) };
1029
1077
  },
1030
1078
  intersection(d) {
package/dist/index.cjs CHANGED
@@ -988,6 +988,46 @@ function isPhantomType(def) {
988
988
 
989
989
  //#endregion
990
990
  //#region packages/typescript/src/json-schema.ts
991
+ /**
992
+ * Detects a discriminator property across union items.
993
+ *
994
+ * Scans all items for object-typed members that share a common property
995
+ * with distinct const/literal values. If exactly one such property exists,
996
+ * it is returned as the discriminator.
997
+ */ function detectDiscriminator(items) {
998
+ if (items.length < 2) return null;
999
+ for (const item of items) if (item.type.kind !== "object") return null;
1000
+ const firstObj = items[0].type;
1001
+ const candidates = [];
1002
+ for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
1003
+ const validCandidates = [];
1004
+ for (const candidate of candidates) {
1005
+ const values = new Set();
1006
+ const mapping = {};
1007
+ let valid = true;
1008
+ for (let i = 0; i < items.length; i++) {
1009
+ const obj = items[i].type;
1010
+ const prop = obj.props.get(candidate);
1011
+ if (!prop || prop.type.kind !== "" || prop.type.value === undefined) {
1012
+ valid = false;
1013
+ break;
1014
+ }
1015
+ const val = prop.type.value;
1016
+ if (values.has(val)) {
1017
+ valid = false;
1018
+ break;
1019
+ }
1020
+ values.add(val);
1021
+ mapping[String(val)] = `#/oneOf/${i}`;
1022
+ }
1023
+ if (valid) validCandidates.push({
1024
+ propertyName: candidate,
1025
+ mapping
1026
+ });
1027
+ }
1028
+ if (validCandidates.length === 1) return validCandidates[0];
1029
+ return null;
1030
+ }
991
1031
  function buildJsonSchema(type) {
992
1032
  const build = (def) => {
993
1033
  const meta = def.metadata;
@@ -1022,6 +1062,14 @@ function buildJsonSchema(type) {
1022
1062
  return schema;
1023
1063
  },
1024
1064
  union(d) {
1065
+ const disc = detectDiscriminator(d.type.items);
1066
+ if (disc) return {
1067
+ oneOf: d.type.items.map(build),
1068
+ discriminator: {
1069
+ propertyName: disc.propertyName,
1070
+ mapping: disc.mapping
1071
+ }
1072
+ };
1025
1073
  return { anyOf: d.type.items.map(build) };
1026
1074
  },
1027
1075
  intersection(d) {
package/dist/index.mjs CHANGED
@@ -964,6 +964,46 @@ function isPhantomType(def) {
964
964
 
965
965
  //#endregion
966
966
  //#region packages/typescript/src/json-schema.ts
967
+ /**
968
+ * Detects a discriminator property across union items.
969
+ *
970
+ * Scans all items for object-typed members that share a common property
971
+ * with distinct const/literal values. If exactly one such property exists,
972
+ * it is returned as the discriminator.
973
+ */ function detectDiscriminator(items) {
974
+ if (items.length < 2) return null;
975
+ for (const item of items) if (item.type.kind !== "object") return null;
976
+ const firstObj = items[0].type;
977
+ const candidates = [];
978
+ for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
979
+ const validCandidates = [];
980
+ for (const candidate of candidates) {
981
+ const values = new Set();
982
+ const mapping = {};
983
+ let valid = true;
984
+ for (let i = 0; i < items.length; i++) {
985
+ const obj = items[i].type;
986
+ const prop = obj.props.get(candidate);
987
+ if (!prop || prop.type.kind !== "" || prop.type.value === undefined) {
988
+ valid = false;
989
+ break;
990
+ }
991
+ const val = prop.type.value;
992
+ if (values.has(val)) {
993
+ valid = false;
994
+ break;
995
+ }
996
+ values.add(val);
997
+ mapping[String(val)] = `#/oneOf/${i}`;
998
+ }
999
+ if (valid) validCandidates.push({
1000
+ propertyName: candidate,
1001
+ mapping
1002
+ });
1003
+ }
1004
+ if (validCandidates.length === 1) return validCandidates[0];
1005
+ return null;
1006
+ }
967
1007
  function buildJsonSchema(type) {
968
1008
  const build = (def) => {
969
1009
  const meta = def.metadata;
@@ -998,6 +1038,14 @@ function buildJsonSchema(type) {
998
1038
  return schema;
999
1039
  },
1000
1040
  union(d) {
1041
+ const disc = detectDiscriminator(d.type.items);
1042
+ if (disc) return {
1043
+ oneOf: d.type.items.map(build),
1044
+ discriminator: {
1045
+ propertyName: disc.propertyName,
1046
+ mapping: disc.mapping
1047
+ }
1048
+ };
1001
1049
  return { anyOf: d.type.items.map(build) };
1002
1050
  },
1003
1051
  intersection(d) {
package/dist/utils.cjs CHANGED
@@ -552,6 +552,46 @@ function isAnnotatedTypeOfPrimitive(t) {
552
552
 
553
553
  //#endregion
554
554
  //#region packages/typescript/src/json-schema.ts
555
+ /**
556
+ * Detects a discriminator property across union items.
557
+ *
558
+ * Scans all items for object-typed members that share a common property
559
+ * with distinct const/literal values. If exactly one such property exists,
560
+ * it is returned as the discriminator.
561
+ */ function detectDiscriminator(items) {
562
+ if (items.length < 2) return null;
563
+ for (const item of items) if (item.type.kind !== "object") return null;
564
+ const firstObj = items[0].type;
565
+ const candidates = [];
566
+ for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
567
+ const validCandidates = [];
568
+ for (const candidate of candidates) {
569
+ const values = new Set();
570
+ const mapping = {};
571
+ let valid = true;
572
+ for (let i = 0; i < items.length; i++) {
573
+ const obj = items[i].type;
574
+ const prop = obj.props.get(candidate);
575
+ if (!prop || prop.type.kind !== "" || prop.type.value === undefined) {
576
+ valid = false;
577
+ break;
578
+ }
579
+ const val = prop.type.value;
580
+ if (values.has(val)) {
581
+ valid = false;
582
+ break;
583
+ }
584
+ values.add(val);
585
+ mapping[String(val)] = `#/oneOf/${i}`;
586
+ }
587
+ if (valid) validCandidates.push({
588
+ propertyName: candidate,
589
+ mapping
590
+ });
591
+ }
592
+ if (validCandidates.length === 1) return validCandidates[0];
593
+ return null;
594
+ }
555
595
  function buildJsonSchema(type) {
556
596
  const build$1 = (def) => {
557
597
  const meta = def.metadata;
@@ -586,6 +626,14 @@ function buildJsonSchema(type) {
586
626
  return schema;
587
627
  },
588
628
  union(d) {
629
+ const disc = detectDiscriminator(d.type.items);
630
+ if (disc) return {
631
+ oneOf: d.type.items.map(build$1),
632
+ discriminator: {
633
+ propertyName: disc.propertyName,
634
+ mapping: disc.mapping
635
+ }
636
+ };
589
637
  return { anyOf: d.type.items.map(build$1) };
590
638
  },
591
639
  intersection(d) {
package/dist/utils.mjs CHANGED
@@ -551,6 +551,46 @@ function isAnnotatedTypeOfPrimitive(t) {
551
551
 
552
552
  //#endregion
553
553
  //#region packages/typescript/src/json-schema.ts
554
+ /**
555
+ * Detects a discriminator property across union items.
556
+ *
557
+ * Scans all items for object-typed members that share a common property
558
+ * with distinct const/literal values. If exactly one such property exists,
559
+ * it is returned as the discriminator.
560
+ */ function detectDiscriminator(items) {
561
+ if (items.length < 2) return null;
562
+ for (const item of items) if (item.type.kind !== "object") return null;
563
+ const firstObj = items[0].type;
564
+ const candidates = [];
565
+ for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
566
+ const validCandidates = [];
567
+ for (const candidate of candidates) {
568
+ const values = new Set();
569
+ const mapping = {};
570
+ let valid = true;
571
+ for (let i = 0; i < items.length; i++) {
572
+ const obj = items[i].type;
573
+ const prop = obj.props.get(candidate);
574
+ if (!prop || prop.type.kind !== "" || prop.type.value === undefined) {
575
+ valid = false;
576
+ break;
577
+ }
578
+ const val = prop.type.value;
579
+ if (values.has(val)) {
580
+ valid = false;
581
+ break;
582
+ }
583
+ values.add(val);
584
+ mapping[String(val)] = `#/oneOf/${i}`;
585
+ }
586
+ if (valid) validCandidates.push({
587
+ propertyName: candidate,
588
+ mapping
589
+ });
590
+ }
591
+ if (validCandidates.length === 1) return validCandidates[0];
592
+ return null;
593
+ }
554
594
  function buildJsonSchema(type) {
555
595
  const build$1 = (def) => {
556
596
  const meta = def.metadata;
@@ -585,6 +625,14 @@ function buildJsonSchema(type) {
585
625
  return schema;
586
626
  },
587
627
  union(d) {
628
+ const disc = detectDiscriminator(d.type.items);
629
+ if (disc) return {
630
+ oneOf: d.type.items.map(build$1),
631
+ discriminator: {
632
+ propertyName: disc.propertyName,
633
+ mapping: disc.mapping
634
+ }
635
+ };
588
636
  return { anyOf: d.type.items.map(build$1) };
589
637
  },
590
638
  intersection(d) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/typescript",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
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.24"
67
+ "@atscript/core": "^0.1.25"
68
68
  },
69
69
  "build": [
70
70
  {},
@@ -88,11 +88,15 @@ const schema = buildJsonSchema(User)
88
88
  | `@expect.pattern` (multiple) | `allOf: [{ pattern }, ...]` |
89
89
  | `@meta.required` on string | `minLength: 1` |
90
90
  | optional property | not in `required` array |
91
- | union | `anyOf` |
91
+ | union | `anyOf` (or `oneOf` + `discriminator` for discriminated unions) |
92
92
  | intersection | `allOf` |
93
93
  | tuple | `items` as array |
94
94
  | phantom | empty object `{}` (excluded) |
95
95
 
96
+ ### Discriminated Unions
97
+
98
+ When all union items are objects sharing exactly one property with distinct const/literal values, `buildJsonSchema` auto-detects it and emits `oneOf` with a `discriminator` object (including `propertyName` and `mapping`) instead of `anyOf`. No annotations needed — detection is automatic.
99
+
96
100
  ## `fromJsonSchema(schema)` — JSON Schema → Annotated Type
97
101
 
98
102
  The inverse of `buildJsonSchema`. Creates a fully functional annotated type from a JSON Schema: