@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 +48 -0
- package/dist/index.cjs +48 -0
- package/dist/index.mjs +48 -0
- package/dist/utils.cjs +48 -0
- package/dist/utils.mjs +48 -0
- package/package.json +2 -2
- package/skills/atscript-typescript/utilities.md +5 -1
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.
|
|
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.
|
|
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:
|