@atscript/typescript 0.1.25 → 0.1.27

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/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, '__esModule', { value: true });
2
3
  //#region rolldown:runtime
3
4
  var __create = Object.create;
4
5
  var __defProp = Object.defineProperty;
@@ -968,6 +969,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
968
969
  __is_atscript_annotated_type: true,
969
970
  type: newBase.type,
970
971
  metadata,
972
+ id: newBase.id,
971
973
  validator(opts) {
972
974
  return new Validator(this, opts);
973
975
  }
@@ -978,6 +980,10 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
978
980
  annotate(key, value, asArray) {
979
981
  annotate(this.$metadata, key, value, asArray);
980
982
  return this;
983
+ },
984
+ id(value) {
985
+ this.$type.id = value;
986
+ return this;
981
987
  }
982
988
  };
983
989
  return handle;
@@ -1029,47 +1035,71 @@ function isPhantomType(def) {
1029
1035
  return null;
1030
1036
  }
1031
1037
  function buildJsonSchema(type) {
1038
+ const defs = {};
1039
+ let isRoot = true;
1040
+ const buildObject = (d) => {
1041
+ const properties = {};
1042
+ const required = [];
1043
+ for (const [key, val] of d.type.props.entries()) {
1044
+ if (isPhantomType(val)) continue;
1045
+ properties[key] = build(val);
1046
+ if (!val.optional) required.push(key);
1047
+ }
1048
+ const schema$1 = {
1049
+ type: "object",
1050
+ properties
1051
+ };
1052
+ if (required.length > 0) schema$1.required = required;
1053
+ return schema$1;
1054
+ };
1032
1055
  const build = (def) => {
1056
+ if (def.id && def.type.kind === "object" && !isRoot) {
1057
+ const name = def.id;
1058
+ if (!defs[name]) {
1059
+ defs[name] = {};
1060
+ defs[name] = buildObject(def);
1061
+ }
1062
+ return { $ref: `#/$defs/${name}` };
1063
+ }
1064
+ isRoot = false;
1033
1065
  const meta = def.metadata;
1034
1066
  return forAnnotatedType(def, {
1035
1067
  phantom() {
1036
1068
  return {};
1037
1069
  },
1038
1070
  object(d) {
1039
- const properties = {};
1040
- const required = [];
1041
- for (const [key, val] of d.type.props.entries()) {
1042
- if (isPhantomType(val)) continue;
1043
- properties[key] = build(val);
1044
- if (!val.optional) required.push(key);
1045
- }
1046
- const schema = {
1047
- type: "object",
1048
- properties
1049
- };
1050
- if (required.length > 0) schema.required = required;
1051
- return schema;
1071
+ return buildObject(d);
1052
1072
  },
1053
1073
  array(d) {
1054
- const schema = {
1074
+ const schema$1 = {
1055
1075
  type: "array",
1056
1076
  items: build(d.type.of)
1057
1077
  };
1058
1078
  const minLength = meta.get("expect.minLength");
1059
- if (minLength) schema.minItems = typeof minLength === "number" ? minLength : minLength.length;
1079
+ if (minLength) schema$1.minItems = typeof minLength === "number" ? minLength : minLength.length;
1060
1080
  const maxLength = meta.get("expect.maxLength");
1061
- if (maxLength) schema.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
1062
- return schema;
1081
+ if (maxLength) schema$1.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
1082
+ return schema$1;
1063
1083
  },
1064
1084
  union(d) {
1065
1085
  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
1086
+ if (disc) {
1087
+ const oneOf = d.type.items.map(build);
1088
+ const mapping = {};
1089
+ for (const [val, origPath] of Object.entries(disc.mapping)) {
1090
+ const idx = Number.parseInt(origPath.split("/").pop(), 10);
1091
+ const item = d.type.items[idx];
1092
+ if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
1093
+ else mapping[val] = origPath;
1071
1094
  }
1072
- };
1095
+ return {
1096
+ oneOf,
1097
+ discriminator: {
1098
+ propertyName: disc.propertyName,
1099
+ mapping
1100
+ }
1101
+ };
1102
+ }
1073
1103
  return { anyOf: d.type.items.map(build) };
1074
1104
  },
1075
1105
  intersection(d) {
@@ -1083,33 +1113,38 @@ function buildJsonSchema(type) {
1083
1113
  };
1084
1114
  },
1085
1115
  final(d) {
1086
- const schema = {};
1087
- if (d.type.value !== undefined) schema.const = d.type.value;
1116
+ const schema$1 = {};
1117
+ if (d.type.value !== undefined) schema$1.const = d.type.value;
1088
1118
  if (d.type.designType && d.type.designType !== "any") {
1089
- schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
1090
- if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
1119
+ schema$1.type = d.type.designType === "undefined" ? "null" : d.type.designType;
1120
+ if (schema$1.type === "number" && meta.get("expect.int")) schema$1.type = "integer";
1091
1121
  }
1092
- if (schema.type === "string") {
1093
- if (meta.get("meta.required")) schema.minLength = 1;
1122
+ if (schema$1.type === "string") {
1123
+ if (meta.get("meta.required")) schema$1.minLength = 1;
1094
1124
  const minLength = meta.get("expect.minLength");
1095
- if (minLength) schema.minLength = typeof minLength === "number" ? minLength : minLength.length;
1125
+ if (minLength) schema$1.minLength = typeof minLength === "number" ? minLength : minLength.length;
1096
1126
  const maxLength = meta.get("expect.maxLength");
1097
- if (maxLength) schema.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
1127
+ if (maxLength) schema$1.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
1098
1128
  const patterns = meta.get("expect.pattern");
1099
- if (patterns?.length) if (patterns.length === 1) schema.pattern = patterns[0].pattern;
1100
- else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
1129
+ if (patterns?.length) if (patterns.length === 1) schema$1.pattern = patterns[0].pattern;
1130
+ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
1101
1131
  }
1102
- if (schema.type === "number" || schema.type === "integer") {
1132
+ if (schema$1.type === "number" || schema$1.type === "integer") {
1103
1133
  const min = meta.get("expect.min");
1104
- if (min) schema.minimum = typeof min === "number" ? min : min.minValue;
1134
+ if (min) schema$1.minimum = typeof min === "number" ? min : min.minValue;
1105
1135
  const max = meta.get("expect.max");
1106
- if (max) schema.maximum = typeof max === "number" ? max : max.maxValue;
1136
+ if (max) schema$1.maximum = typeof max === "number" ? max : max.maxValue;
1107
1137
  }
1108
- return schema;
1138
+ return schema$1;
1109
1139
  }
1110
1140
  });
1111
1141
  };
1112
- return build(type);
1142
+ const schema = build(type);
1143
+ if (Object.keys(defs).length > 0) return {
1144
+ ...schema,
1145
+ $defs: defs
1146
+ };
1147
+ return schema;
1113
1148
  }
1114
1149
 
1115
1150
  //#endregion
@@ -1130,11 +1165,23 @@ var JsRenderer = class extends BaseRenderer {
1130
1165
  this.writeln("/* eslint-disable */");
1131
1166
  this.writeln("/* oxlint-disable */");
1132
1167
  const imports = ["defineAnnotatedType as $", "annotate as $a"];
1168
+ const hasMutatingAnnotate = this.doc.nodes.some((n) => n.entity === "annotate" && n.isMutating);
1169
+ if (hasMutatingAnnotate) imports.push("cloneRefProp as $c");
1133
1170
  const jsonSchemaMode = resolveJsonSchemaMode(this.opts);
1134
1171
  if (jsonSchemaMode === "lazy") imports.push("buildJsonSchema as $$");
1135
1172
  if (this.opts?.exampleData) imports.push("createDataFromAnnotatedType as $e");
1136
1173
  if (jsonSchemaMode === false) imports.push("throwFeatureDisabled as $d");
1137
1174
  this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
1175
+ const nameCounts = new Map();
1176
+ const nodesByName = new Map();
1177
+ for (const node of this.doc.nodes) if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
1178
+ const name = node.id;
1179
+ nameCounts.set(name, (nameCounts.get(name) || 0) + 1);
1180
+ if (!nodesByName.has(name)) nodesByName.set(name, []);
1181
+ nodesByName.get(name).push(node);
1182
+ }
1183
+ for (const [name, nodes] of nodesByName) if (nodes.length === 1) this.typeIds.set(nodes[0], name);
1184
+ else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}__${i + 1}`);
1138
1185
  }
1139
1186
  buildAdHocMap(annotateNodes) {
1140
1187
  const map = new Map();
@@ -1148,6 +1195,15 @@ var JsRenderer = class extends BaseRenderer {
1148
1195
  }
1149
1196
  return map.size > 0 ? map : null;
1150
1197
  }
1198
+ /**
1199
+ * Checks if any ad-hoc annotation path extends beyond the current _propPath,
1200
+ * meaning annotations target properties inside a referenced type.
1201
+ */ hasAdHocAnnotationsThroughRef() {
1202
+ if (!this._adHocAnnotations || this._propPath.length === 0) return false;
1203
+ const prefix = `${this._propPath.join(".")}.`;
1204
+ for (const key of this._adHocAnnotations.keys()) if (key.startsWith(prefix)) return true;
1205
+ return false;
1206
+ }
1151
1207
  post() {
1152
1208
  for (const node of this.postAnnotate) if (node.entity === "annotate") {
1153
1209
  const annotateNode = node;
@@ -1178,6 +1234,8 @@ else {
1178
1234
  this.writeln("static __is_atscript_annotated_type = true");
1179
1235
  this.writeln("static type = {}");
1180
1236
  this.writeln("static metadata = new Map()");
1237
+ const typeId = this.typeIds.get(node);
1238
+ if (typeId) this.writeln(`static id = "${typeId}"`);
1181
1239
  this.renderJsonSchemaMethod(node);
1182
1240
  this.renderExampleDataMethod(node);
1183
1241
  }
@@ -1256,6 +1314,8 @@ else {
1256
1314
  case "type": {
1257
1315
  const def = node.getDefinition();
1258
1316
  const handle = this.toAnnotatedHandle(def, true);
1317
+ const typeId = this.typeIds.get(node) ?? (node.__typeId !== null && node.__typeId !== undefined ? node.id : undefined);
1318
+ if (typeId) handle.id(typeId);
1259
1319
  return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
1260
1320
  }
1261
1321
  case "prop": {
@@ -1382,6 +1442,14 @@ else handle.prop(prop.id, propHandle.$type);
1382
1442
  return this;
1383
1443
  }
1384
1444
  }
1445
+ if (this._adHocAnnotations && this.hasAdHocAnnotationsThroughRef()) {
1446
+ let resolved = decl ? this.doc.mergeIntersection(decl) : undefined;
1447
+ if (resolved && (0, __atscript_core.isInterface)(resolved)) resolved = resolved.getDefinition() || resolved;
1448
+ if (resolved) {
1449
+ this.annotateType(resolved, name);
1450
+ return this;
1451
+ }
1452
+ }
1385
1453
  const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
1386
1454
  this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`);
1387
1455
  if (!ref.hasChain) {
@@ -1615,11 +1683,29 @@ else targetValue = "true";
1615
1683
  const targetName = node.targetName;
1616
1684
  const targetDef = this.resolveTargetDef(targetName);
1617
1685
  this.writeln("// Ad-hoc annotations for ", targetName);
1686
+ const allClones = [];
1687
+ const entryAccessors = [];
1618
1688
  for (const entry of node.entries) {
1619
1689
  const anns = entry.annotations;
1620
1690
  if (!anns || anns.length === 0) continue;
1621
1691
  const parts = entry.hasChain ? [entry.id, ...entry.chain.map((c) => c.text)] : [entry.id];
1622
- const accessors = this.buildMutatingAccessors(targetName, targetDef, parts);
1692
+ const { accessors, clones } = this.buildMutatingAccessors(targetName, targetDef, parts);
1693
+ allClones.push(...clones);
1694
+ entryAccessors.push({
1695
+ entry,
1696
+ accessors
1697
+ });
1698
+ }
1699
+ const cloneKeys = new Set();
1700
+ for (const clone of allClones) {
1701
+ const key = `${clone.parentPath}|${clone.propName}`;
1702
+ if (!cloneKeys.has(key)) {
1703
+ cloneKeys.add(key);
1704
+ this.writeln(`$c(${clone.parentPath}, "${escapeQuotes(clone.propName)}")`);
1705
+ }
1706
+ }
1707
+ for (const { entry, accessors } of entryAccessors) {
1708
+ const anns = entry.annotations;
1623
1709
  for (const accessor of accessors) {
1624
1710
  const cleared = new Set();
1625
1711
  for (const an of anns) {
@@ -1669,15 +1755,21 @@ else targetValue = "true";
1669
1755
  prefix: `${targetName}.type`,
1670
1756
  def: targetDef
1671
1757
  }];
1758
+ const clones = [];
1672
1759
  for (let i = 0; i < parts.length; i++) {
1673
1760
  const nextAccessors = [];
1674
1761
  for (const { prefix, def } of accessors) {
1675
1762
  const results = this.buildPropPaths(def, parts[i]);
1676
- if (results.length > 0) for (const result of results) if (i < parts.length - 1) nextAccessors.push({
1677
- prefix: `${prefix}${result.path}?.type`,
1678
- def: result.propDef
1679
- });
1680
- else nextAccessors.push({
1763
+ if (results.length > 0) for (const result of results) if (i < parts.length - 1) {
1764
+ if (result.propDef && (0, __atscript_core.isRef)(result.propDef)) clones.push({
1765
+ parentPath: prefix,
1766
+ propName: parts[i]
1767
+ });
1768
+ nextAccessors.push({
1769
+ prefix: `${prefix}${result.path}?.type`,
1770
+ def: result.propDef
1771
+ });
1772
+ } else nextAccessors.push({
1681
1773
  prefix: `${prefix}${result.path}?`,
1682
1774
  def: result.propDef
1683
1775
  });
@@ -1691,7 +1783,10 @@ else {
1691
1783
  }
1692
1784
  accessors = nextAccessors;
1693
1785
  }
1694
- return accessors.map((a) => a.prefix);
1786
+ return {
1787
+ accessors: accessors.map((a) => a.prefix),
1788
+ clones
1789
+ };
1695
1790
  }
1696
1791
  /**
1697
1792
  * Finds a property in a type tree at compile time, returning all
@@ -1727,7 +1822,7 @@ else {
1727
1822
  return [];
1728
1823
  }
1729
1824
  constructor(doc, opts) {
1730
- super(doc), _define_property(this, "opts", void 0), _define_property(this, "postAnnotate", void 0), _define_property(this, "_adHocAnnotations", void 0), _define_property(this, "_propPath", void 0), this.opts = opts, this.postAnnotate = [], this._adHocAnnotations = null, this._propPath = [];
1825
+ super(doc), _define_property(this, "opts", void 0), _define_property(this, "postAnnotate", void 0), _define_property(this, "_adHocAnnotations", void 0), _define_property(this, "_propPath", void 0), _define_property(this, "typeIds", void 0), this.opts = opts, this.postAnnotate = [], this._adHocAnnotations = null, this._propPath = [], this.typeIds = new Map();
1731
1826
  }
1732
1827
  };
1733
1828
 
@@ -1776,8 +1871,5 @@ else return t.optional ? `${t.type} | true` : t.type;
1776
1871
  };
1777
1872
 
1778
1873
  //#endregion
1779
- //#region packages/typescript/src/index.ts
1780
- var src_default = tsPlugin;
1781
-
1782
- //#endregion
1783
- module.exports = src_default;
1874
+ exports.default = tsPlugin
1875
+ exports.tsPlugin = tsPlugin
package/dist/index.d.ts CHANGED
@@ -20,4 +20,4 @@ interface TTsPluginOptions {
20
20
  }
21
21
  declare const tsPlugin: (opts?: TTsPluginOptions) => TAtscriptPlugin;
22
22
 
23
- export { tsPlugin as default };
23
+ export { tsPlugin as default, tsPlugin };
package/dist/index.mjs CHANGED
@@ -944,6 +944,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
944
944
  __is_atscript_annotated_type: true,
945
945
  type: newBase.type,
946
946
  metadata,
947
+ id: newBase.id,
947
948
  validator(opts) {
948
949
  return new Validator(this, opts);
949
950
  }
@@ -954,6 +955,10 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
954
955
  annotate(key, value, asArray) {
955
956
  annotate(this.$metadata, key, value, asArray);
956
957
  return this;
958
+ },
959
+ id(value) {
960
+ this.$type.id = value;
961
+ return this;
957
962
  }
958
963
  };
959
964
  return handle;
@@ -1005,47 +1010,71 @@ function isPhantomType(def) {
1005
1010
  return null;
1006
1011
  }
1007
1012
  function buildJsonSchema(type) {
1013
+ const defs = {};
1014
+ let isRoot = true;
1015
+ const buildObject = (d) => {
1016
+ const properties = {};
1017
+ const required = [];
1018
+ for (const [key, val] of d.type.props.entries()) {
1019
+ if (isPhantomType(val)) continue;
1020
+ properties[key] = build(val);
1021
+ if (!val.optional) required.push(key);
1022
+ }
1023
+ const schema$1 = {
1024
+ type: "object",
1025
+ properties
1026
+ };
1027
+ if (required.length > 0) schema$1.required = required;
1028
+ return schema$1;
1029
+ };
1008
1030
  const build = (def) => {
1031
+ if (def.id && def.type.kind === "object" && !isRoot) {
1032
+ const name = def.id;
1033
+ if (!defs[name]) {
1034
+ defs[name] = {};
1035
+ defs[name] = buildObject(def);
1036
+ }
1037
+ return { $ref: `#/$defs/${name}` };
1038
+ }
1039
+ isRoot = false;
1009
1040
  const meta = def.metadata;
1010
1041
  return forAnnotatedType(def, {
1011
1042
  phantom() {
1012
1043
  return {};
1013
1044
  },
1014
1045
  object(d) {
1015
- const properties = {};
1016
- const required = [];
1017
- for (const [key, val] of d.type.props.entries()) {
1018
- if (isPhantomType(val)) continue;
1019
- properties[key] = build(val);
1020
- if (!val.optional) required.push(key);
1021
- }
1022
- const schema = {
1023
- type: "object",
1024
- properties
1025
- };
1026
- if (required.length > 0) schema.required = required;
1027
- return schema;
1046
+ return buildObject(d);
1028
1047
  },
1029
1048
  array(d) {
1030
- const schema = {
1049
+ const schema$1 = {
1031
1050
  type: "array",
1032
1051
  items: build(d.type.of)
1033
1052
  };
1034
1053
  const minLength = meta.get("expect.minLength");
1035
- if (minLength) schema.minItems = typeof minLength === "number" ? minLength : minLength.length;
1054
+ if (minLength) schema$1.minItems = typeof minLength === "number" ? minLength : minLength.length;
1036
1055
  const maxLength = meta.get("expect.maxLength");
1037
- if (maxLength) schema.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
1038
- return schema;
1056
+ if (maxLength) schema$1.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
1057
+ return schema$1;
1039
1058
  },
1040
1059
  union(d) {
1041
1060
  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
1061
+ if (disc) {
1062
+ const oneOf = d.type.items.map(build);
1063
+ const mapping = {};
1064
+ for (const [val, origPath] of Object.entries(disc.mapping)) {
1065
+ const idx = Number.parseInt(origPath.split("/").pop(), 10);
1066
+ const item = d.type.items[idx];
1067
+ if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
1068
+ else mapping[val] = origPath;
1047
1069
  }
1048
- };
1070
+ return {
1071
+ oneOf,
1072
+ discriminator: {
1073
+ propertyName: disc.propertyName,
1074
+ mapping
1075
+ }
1076
+ };
1077
+ }
1049
1078
  return { anyOf: d.type.items.map(build) };
1050
1079
  },
1051
1080
  intersection(d) {
@@ -1059,33 +1088,38 @@ function buildJsonSchema(type) {
1059
1088
  };
1060
1089
  },
1061
1090
  final(d) {
1062
- const schema = {};
1063
- if (d.type.value !== undefined) schema.const = d.type.value;
1091
+ const schema$1 = {};
1092
+ if (d.type.value !== undefined) schema$1.const = d.type.value;
1064
1093
  if (d.type.designType && d.type.designType !== "any") {
1065
- schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
1066
- if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
1094
+ schema$1.type = d.type.designType === "undefined" ? "null" : d.type.designType;
1095
+ if (schema$1.type === "number" && meta.get("expect.int")) schema$1.type = "integer";
1067
1096
  }
1068
- if (schema.type === "string") {
1069
- if (meta.get("meta.required")) schema.minLength = 1;
1097
+ if (schema$1.type === "string") {
1098
+ if (meta.get("meta.required")) schema$1.minLength = 1;
1070
1099
  const minLength = meta.get("expect.minLength");
1071
- if (minLength) schema.minLength = typeof minLength === "number" ? minLength : minLength.length;
1100
+ if (minLength) schema$1.minLength = typeof minLength === "number" ? minLength : minLength.length;
1072
1101
  const maxLength = meta.get("expect.maxLength");
1073
- if (maxLength) schema.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
1102
+ if (maxLength) schema$1.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
1074
1103
  const patterns = meta.get("expect.pattern");
1075
- if (patterns?.length) if (patterns.length === 1) schema.pattern = patterns[0].pattern;
1076
- else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
1104
+ if (patterns?.length) if (patterns.length === 1) schema$1.pattern = patterns[0].pattern;
1105
+ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
1077
1106
  }
1078
- if (schema.type === "number" || schema.type === "integer") {
1107
+ if (schema$1.type === "number" || schema$1.type === "integer") {
1079
1108
  const min = meta.get("expect.min");
1080
- if (min) schema.minimum = typeof min === "number" ? min : min.minValue;
1109
+ if (min) schema$1.minimum = typeof min === "number" ? min : min.minValue;
1081
1110
  const max = meta.get("expect.max");
1082
- if (max) schema.maximum = typeof max === "number" ? max : max.maxValue;
1111
+ if (max) schema$1.maximum = typeof max === "number" ? max : max.maxValue;
1083
1112
  }
1084
- return schema;
1113
+ return schema$1;
1085
1114
  }
1086
1115
  });
1087
1116
  };
1088
- return build(type);
1117
+ const schema = build(type);
1118
+ if (Object.keys(defs).length > 0) return {
1119
+ ...schema,
1120
+ $defs: defs
1121
+ };
1122
+ return schema;
1089
1123
  }
1090
1124
 
1091
1125
  //#endregion
@@ -1106,11 +1140,23 @@ var JsRenderer = class extends BaseRenderer {
1106
1140
  this.writeln("/* eslint-disable */");
1107
1141
  this.writeln("/* oxlint-disable */");
1108
1142
  const imports = ["defineAnnotatedType as $", "annotate as $a"];
1143
+ const hasMutatingAnnotate = this.doc.nodes.some((n) => n.entity === "annotate" && n.isMutating);
1144
+ if (hasMutatingAnnotate) imports.push("cloneRefProp as $c");
1109
1145
  const jsonSchemaMode = resolveJsonSchemaMode(this.opts);
1110
1146
  if (jsonSchemaMode === "lazy") imports.push("buildJsonSchema as $$");
1111
1147
  if (this.opts?.exampleData) imports.push("createDataFromAnnotatedType as $e");
1112
1148
  if (jsonSchemaMode === false) imports.push("throwFeatureDisabled as $d");
1113
1149
  this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
1150
+ const nameCounts = new Map();
1151
+ const nodesByName = new Map();
1152
+ for (const node of this.doc.nodes) if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
1153
+ const name = node.id;
1154
+ nameCounts.set(name, (nameCounts.get(name) || 0) + 1);
1155
+ if (!nodesByName.has(name)) nodesByName.set(name, []);
1156
+ nodesByName.get(name).push(node);
1157
+ }
1158
+ for (const [name, nodes] of nodesByName) if (nodes.length === 1) this.typeIds.set(nodes[0], name);
1159
+ else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}__${i + 1}`);
1114
1160
  }
1115
1161
  buildAdHocMap(annotateNodes) {
1116
1162
  const map = new Map();
@@ -1124,6 +1170,15 @@ var JsRenderer = class extends BaseRenderer {
1124
1170
  }
1125
1171
  return map.size > 0 ? map : null;
1126
1172
  }
1173
+ /**
1174
+ * Checks if any ad-hoc annotation path extends beyond the current _propPath,
1175
+ * meaning annotations target properties inside a referenced type.
1176
+ */ hasAdHocAnnotationsThroughRef() {
1177
+ if (!this._adHocAnnotations || this._propPath.length === 0) return false;
1178
+ const prefix = `${this._propPath.join(".")}.`;
1179
+ for (const key of this._adHocAnnotations.keys()) if (key.startsWith(prefix)) return true;
1180
+ return false;
1181
+ }
1127
1182
  post() {
1128
1183
  for (const node of this.postAnnotate) if (node.entity === "annotate") {
1129
1184
  const annotateNode = node;
@@ -1154,6 +1209,8 @@ else {
1154
1209
  this.writeln("static __is_atscript_annotated_type = true");
1155
1210
  this.writeln("static type = {}");
1156
1211
  this.writeln("static metadata = new Map()");
1212
+ const typeId = this.typeIds.get(node);
1213
+ if (typeId) this.writeln(`static id = "${typeId}"`);
1157
1214
  this.renderJsonSchemaMethod(node);
1158
1215
  this.renderExampleDataMethod(node);
1159
1216
  }
@@ -1232,6 +1289,8 @@ else {
1232
1289
  case "type": {
1233
1290
  const def = node.getDefinition();
1234
1291
  const handle = this.toAnnotatedHandle(def, true);
1292
+ const typeId = this.typeIds.get(node) ?? (node.__typeId !== null && node.__typeId !== undefined ? node.id : undefined);
1293
+ if (typeId) handle.id(typeId);
1235
1294
  return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
1236
1295
  }
1237
1296
  case "prop": {
@@ -1358,6 +1417,14 @@ else handle.prop(prop.id, propHandle.$type);
1358
1417
  return this;
1359
1418
  }
1360
1419
  }
1420
+ if (this._adHocAnnotations && this.hasAdHocAnnotationsThroughRef()) {
1421
+ let resolved = decl ? this.doc.mergeIntersection(decl) : undefined;
1422
+ if (resolved && isInterface(resolved)) resolved = resolved.getDefinition() || resolved;
1423
+ if (resolved) {
1424
+ this.annotateType(resolved, name);
1425
+ return this;
1426
+ }
1427
+ }
1361
1428
  const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
1362
1429
  this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`);
1363
1430
  if (!ref.hasChain) {
@@ -1591,11 +1658,29 @@ else targetValue = "true";
1591
1658
  const targetName = node.targetName;
1592
1659
  const targetDef = this.resolveTargetDef(targetName);
1593
1660
  this.writeln("// Ad-hoc annotations for ", targetName);
1661
+ const allClones = [];
1662
+ const entryAccessors = [];
1594
1663
  for (const entry of node.entries) {
1595
1664
  const anns = entry.annotations;
1596
1665
  if (!anns || anns.length === 0) continue;
1597
1666
  const parts = entry.hasChain ? [entry.id, ...entry.chain.map((c) => c.text)] : [entry.id];
1598
- const accessors = this.buildMutatingAccessors(targetName, targetDef, parts);
1667
+ const { accessors, clones } = this.buildMutatingAccessors(targetName, targetDef, parts);
1668
+ allClones.push(...clones);
1669
+ entryAccessors.push({
1670
+ entry,
1671
+ accessors
1672
+ });
1673
+ }
1674
+ const cloneKeys = new Set();
1675
+ for (const clone of allClones) {
1676
+ const key = `${clone.parentPath}|${clone.propName}`;
1677
+ if (!cloneKeys.has(key)) {
1678
+ cloneKeys.add(key);
1679
+ this.writeln(`$c(${clone.parentPath}, "${escapeQuotes(clone.propName)}")`);
1680
+ }
1681
+ }
1682
+ for (const { entry, accessors } of entryAccessors) {
1683
+ const anns = entry.annotations;
1599
1684
  for (const accessor of accessors) {
1600
1685
  const cleared = new Set();
1601
1686
  for (const an of anns) {
@@ -1645,15 +1730,21 @@ else targetValue = "true";
1645
1730
  prefix: `${targetName}.type`,
1646
1731
  def: targetDef
1647
1732
  }];
1733
+ const clones = [];
1648
1734
  for (let i = 0; i < parts.length; i++) {
1649
1735
  const nextAccessors = [];
1650
1736
  for (const { prefix, def } of accessors) {
1651
1737
  const results = this.buildPropPaths(def, parts[i]);
1652
- if (results.length > 0) for (const result of results) if (i < parts.length - 1) nextAccessors.push({
1653
- prefix: `${prefix}${result.path}?.type`,
1654
- def: result.propDef
1655
- });
1656
- else nextAccessors.push({
1738
+ if (results.length > 0) for (const result of results) if (i < parts.length - 1) {
1739
+ if (result.propDef && isRef(result.propDef)) clones.push({
1740
+ parentPath: prefix,
1741
+ propName: parts[i]
1742
+ });
1743
+ nextAccessors.push({
1744
+ prefix: `${prefix}${result.path}?.type`,
1745
+ def: result.propDef
1746
+ });
1747
+ } else nextAccessors.push({
1657
1748
  prefix: `${prefix}${result.path}?`,
1658
1749
  def: result.propDef
1659
1750
  });
@@ -1667,7 +1758,10 @@ else {
1667
1758
  }
1668
1759
  accessors = nextAccessors;
1669
1760
  }
1670
- return accessors.map((a) => a.prefix);
1761
+ return {
1762
+ accessors: accessors.map((a) => a.prefix),
1763
+ clones
1764
+ };
1671
1765
  }
1672
1766
  /**
1673
1767
  * Finds a property in a type tree at compile time, returning all
@@ -1703,7 +1797,7 @@ else {
1703
1797
  return [];
1704
1798
  }
1705
1799
  constructor(doc, opts) {
1706
- super(doc), _define_property(this, "opts", void 0), _define_property(this, "postAnnotate", void 0), _define_property(this, "_adHocAnnotations", void 0), _define_property(this, "_propPath", void 0), this.opts = opts, this.postAnnotate = [], this._adHocAnnotations = null, this._propPath = [];
1800
+ super(doc), _define_property(this, "opts", void 0), _define_property(this, "postAnnotate", void 0), _define_property(this, "_adHocAnnotations", void 0), _define_property(this, "_propPath", void 0), _define_property(this, "typeIds", void 0), this.opts = opts, this.postAnnotate = [], this._adHocAnnotations = null, this._propPath = [], this.typeIds = new Map();
1707
1801
  }
1708
1802
  };
1709
1803
 
@@ -1752,8 +1846,4 @@ else return t.optional ? `${t.type} | true` : t.type;
1752
1846
  };
1753
1847
 
1754
1848
  //#endregion
1755
- //#region packages/typescript/src/index.ts
1756
- var src_default = tsPlugin;
1757
-
1758
- //#endregion
1759
- export { src_default as default };
1849
+ export { tsPlugin as default, tsPlugin };