@atscript/typescript 0.1.33 → 0.1.34

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
@@ -1058,7 +1058,8 @@ function createAnnotatedTypeNode(type, metadata, opts) {
1058
1058
  metadata,
1059
1059
  validator: validatorMethod,
1060
1060
  id: opts?.id,
1061
- optional: opts?.optional
1061
+ optional: opts?.optional,
1062
+ ref: opts?.ref
1062
1063
  };
1063
1064
  }
1064
1065
  function isAnnotatedType(type) {
@@ -1135,18 +1136,57 @@ function defineAnnotatedType(_kind, base) {
1135
1136
  return this;
1136
1137
  },
1137
1138
  refTo(type$1, chain) {
1138
- if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
1139
- let newBase = type$1;
1140
- const typeName = type$1.name || "Unknown";
1141
- if (chain) for (let i = 0; i < chain.length; i++) {
1142
- const c = chain[i];
1143
- if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
1139
+ if (isAnnotatedType(type$1)) {
1140
+ let newBase = type$1;
1141
+ const typeName = type$1.name || "Unknown";
1142
+ if (chain) for (let i = 0; i < chain.length; i++) {
1143
+ const c = chain[i];
1144
+ if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
1144
1145
  else {
1145
- const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
1146
- throw new Error(`Can't find prop ${typeName}${keys}`);
1146
+ const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
1147
+ throw new Error(`Can't find prop ${typeName}${keys}`);
1148
+ }
1147
1149
  }
1148
- }
1149
- this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
1150
+ this.$type = createAnnotatedTypeNode(newBase.type, metadata, {
1151
+ id: newBase.id,
1152
+ ref: chain && chain.length > 0 ? {
1153
+ type: () => type$1,
1154
+ field: chain.join(".")
1155
+ } : undefined
1156
+ });
1157
+ } else if (typeof type$1 === "function") {
1158
+ const lazyType = type$1;
1159
+ this.$type = createAnnotatedTypeNode({ kind: "" }, metadata, { ref: {
1160
+ type: lazyType,
1161
+ field: chain ? chain.join(".") : ""
1162
+ } });
1163
+ const node = this.$type;
1164
+ const placeholder = node.type;
1165
+ Object.defineProperty(node, "type", {
1166
+ get() {
1167
+ const t = lazyType();
1168
+ if (!isAnnotatedType(t)) {
1169
+ Object.defineProperty(node, "type", {
1170
+ value: placeholder,
1171
+ writable: false,
1172
+ configurable: true
1173
+ });
1174
+ return placeholder;
1175
+ }
1176
+ let target = t;
1177
+ if (chain) for (const c of chain) if (target.type.kind === "object" && target.type.props.has(c)) target = target.type.props.get(c);
1178
+ else return t.type;
1179
+ node.id = target.id || t.id;
1180
+ Object.defineProperty(node, "type", {
1181
+ value: target.type,
1182
+ writable: false,
1183
+ configurable: true
1184
+ });
1185
+ return target.type;
1186
+ },
1187
+ configurable: true
1188
+ });
1189
+ } else throw new Error(`${type$1} is not annotated type`);
1150
1190
  return this;
1151
1191
  },
1152
1192
  annotate(key, value, asArray) {
@@ -1631,8 +1671,8 @@ else handle.prop(prop.id, propHandle.$type);
1631
1671
  const ref = node;
1632
1672
  const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
1633
1673
  if ((0, __atscript_core.isPrimitive)(decl)) {
1634
- const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1635
- if (!ownerDecl?.node || ownerDecl.node.entity !== "type" && ownerDecl.node.entity !== "interface") {
1674
+ const ownerDecl$1 = this.doc.getDeclarationOwnerNode(ref.id);
1675
+ if (!ownerDecl$1?.node || ownerDecl$1.node.entity !== "type" && ownerDecl$1.node.entity !== "interface") {
1636
1676
  this.annotateType(decl, name);
1637
1677
  return this;
1638
1678
  }
@@ -1646,9 +1686,11 @@ else handle.prop(prop.id, propHandle.$type);
1646
1686
  }
1647
1687
  }
1648
1688
  const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
1649
- this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`);
1689
+ const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1690
+ const isImported = ownerDecl ? ownerDecl.doc !== this.doc : false;
1691
+ const refExpr = isImported ? `() => ${ref.id}` : ref.id;
1692
+ this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${refExpr}${chain})`);
1650
1693
  if (!ref.hasChain) {
1651
- const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1652
1694
  if (ownerDecl?.node) {
1653
1695
  const typeAnnotations = ownerDecl.doc.evalAnnotationsForNode(ownerDecl.node);
1654
1696
  typeAnnotations?.forEach((an) => {
package/dist/index.cjs CHANGED
@@ -1056,7 +1056,8 @@ function createAnnotatedTypeNode(type, metadata, opts) {
1056
1056
  metadata,
1057
1057
  validator: validatorMethod,
1058
1058
  id: opts?.id,
1059
- optional: opts?.optional
1059
+ optional: opts?.optional,
1060
+ ref: opts?.ref
1060
1061
  };
1061
1062
  }
1062
1063
  function isAnnotatedType(type) {
@@ -1133,18 +1134,57 @@ function defineAnnotatedType(_kind, base) {
1133
1134
  return this;
1134
1135
  },
1135
1136
  refTo(type$1, chain) {
1136
- if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
1137
- let newBase = type$1;
1138
- const typeName = type$1.name || "Unknown";
1139
- if (chain) for (let i = 0; i < chain.length; i++) {
1140
- const c = chain[i];
1141
- if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
1137
+ if (isAnnotatedType(type$1)) {
1138
+ let newBase = type$1;
1139
+ const typeName = type$1.name || "Unknown";
1140
+ if (chain) for (let i = 0; i < chain.length; i++) {
1141
+ const c = chain[i];
1142
+ if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
1142
1143
  else {
1143
- const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
1144
- throw new Error(`Can't find prop ${typeName}${keys}`);
1144
+ const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
1145
+ throw new Error(`Can't find prop ${typeName}${keys}`);
1146
+ }
1145
1147
  }
1146
- }
1147
- this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
1148
+ this.$type = createAnnotatedTypeNode(newBase.type, metadata, {
1149
+ id: newBase.id,
1150
+ ref: chain && chain.length > 0 ? {
1151
+ type: () => type$1,
1152
+ field: chain.join(".")
1153
+ } : undefined
1154
+ });
1155
+ } else if (typeof type$1 === "function") {
1156
+ const lazyType = type$1;
1157
+ this.$type = createAnnotatedTypeNode({ kind: "" }, metadata, { ref: {
1158
+ type: lazyType,
1159
+ field: chain ? chain.join(".") : ""
1160
+ } });
1161
+ const node = this.$type;
1162
+ const placeholder = node.type;
1163
+ Object.defineProperty(node, "type", {
1164
+ get() {
1165
+ const t = lazyType();
1166
+ if (!isAnnotatedType(t)) {
1167
+ Object.defineProperty(node, "type", {
1168
+ value: placeholder,
1169
+ writable: false,
1170
+ configurable: true
1171
+ });
1172
+ return placeholder;
1173
+ }
1174
+ let target = t;
1175
+ if (chain) for (const c of chain) if (target.type.kind === "object" && target.type.props.has(c)) target = target.type.props.get(c);
1176
+ else return t.type;
1177
+ node.id = target.id || t.id;
1178
+ Object.defineProperty(node, "type", {
1179
+ value: target.type,
1180
+ writable: false,
1181
+ configurable: true
1182
+ });
1183
+ return target.type;
1184
+ },
1185
+ configurable: true
1186
+ });
1187
+ } else throw new Error(`${type$1} is not annotated type`);
1148
1188
  return this;
1149
1189
  },
1150
1190
  annotate(key, value, asArray) {
@@ -1629,8 +1669,8 @@ else handle.prop(prop.id, propHandle.$type);
1629
1669
  const ref = node;
1630
1670
  const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
1631
1671
  if ((0, __atscript_core.isPrimitive)(decl)) {
1632
- const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1633
- if (!ownerDecl?.node || ownerDecl.node.entity !== "type" && ownerDecl.node.entity !== "interface") {
1672
+ const ownerDecl$1 = this.doc.getDeclarationOwnerNode(ref.id);
1673
+ if (!ownerDecl$1?.node || ownerDecl$1.node.entity !== "type" && ownerDecl$1.node.entity !== "interface") {
1634
1674
  this.annotateType(decl, name);
1635
1675
  return this;
1636
1676
  }
@@ -1644,9 +1684,11 @@ else handle.prop(prop.id, propHandle.$type);
1644
1684
  }
1645
1685
  }
1646
1686
  const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
1647
- this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`);
1687
+ const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1688
+ const isImported = ownerDecl ? ownerDecl.doc !== this.doc : false;
1689
+ const refExpr = isImported ? `() => ${ref.id}` : ref.id;
1690
+ this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${refExpr}${chain})`);
1648
1691
  if (!ref.hasChain) {
1649
- const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1650
1692
  if (ownerDecl?.node) {
1651
1693
  const typeAnnotations = ownerDecl.doc.evalAnnotationsForNode(ownerDecl.node);
1652
1694
  typeAnnotations?.forEach((an) => {
package/dist/index.mjs CHANGED
@@ -1031,7 +1031,8 @@ function createAnnotatedTypeNode(type, metadata, opts) {
1031
1031
  metadata,
1032
1032
  validator: validatorMethod,
1033
1033
  id: opts?.id,
1034
- optional: opts?.optional
1034
+ optional: opts?.optional,
1035
+ ref: opts?.ref
1035
1036
  };
1036
1037
  }
1037
1038
  function isAnnotatedType(type) {
@@ -1108,18 +1109,57 @@ function defineAnnotatedType(_kind, base) {
1108
1109
  return this;
1109
1110
  },
1110
1111
  refTo(type$1, chain) {
1111
- if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
1112
- let newBase = type$1;
1113
- const typeName = type$1.name || "Unknown";
1114
- if (chain) for (let i = 0; i < chain.length; i++) {
1115
- const c = chain[i];
1116
- if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
1112
+ if (isAnnotatedType(type$1)) {
1113
+ let newBase = type$1;
1114
+ const typeName = type$1.name || "Unknown";
1115
+ if (chain) for (let i = 0; i < chain.length; i++) {
1116
+ const c = chain[i];
1117
+ if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
1117
1118
  else {
1118
- const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
1119
- throw new Error(`Can't find prop ${typeName}${keys}`);
1119
+ const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
1120
+ throw new Error(`Can't find prop ${typeName}${keys}`);
1121
+ }
1120
1122
  }
1121
- }
1122
- this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
1123
+ this.$type = createAnnotatedTypeNode(newBase.type, metadata, {
1124
+ id: newBase.id,
1125
+ ref: chain && chain.length > 0 ? {
1126
+ type: () => type$1,
1127
+ field: chain.join(".")
1128
+ } : undefined
1129
+ });
1130
+ } else if (typeof type$1 === "function") {
1131
+ const lazyType = type$1;
1132
+ this.$type = createAnnotatedTypeNode({ kind: "" }, metadata, { ref: {
1133
+ type: lazyType,
1134
+ field: chain ? chain.join(".") : ""
1135
+ } });
1136
+ const node = this.$type;
1137
+ const placeholder = node.type;
1138
+ Object.defineProperty(node, "type", {
1139
+ get() {
1140
+ const t = lazyType();
1141
+ if (!isAnnotatedType(t)) {
1142
+ Object.defineProperty(node, "type", {
1143
+ value: placeholder,
1144
+ writable: false,
1145
+ configurable: true
1146
+ });
1147
+ return placeholder;
1148
+ }
1149
+ let target = t;
1150
+ if (chain) for (const c of chain) if (target.type.kind === "object" && target.type.props.has(c)) target = target.type.props.get(c);
1151
+ else return t.type;
1152
+ node.id = target.id || t.id;
1153
+ Object.defineProperty(node, "type", {
1154
+ value: target.type,
1155
+ writable: false,
1156
+ configurable: true
1157
+ });
1158
+ return target.type;
1159
+ },
1160
+ configurable: true
1161
+ });
1162
+ } else throw new Error(`${type$1} is not annotated type`);
1123
1163
  return this;
1124
1164
  },
1125
1165
  annotate(key, value, asArray) {
@@ -1604,8 +1644,8 @@ else handle.prop(prop.id, propHandle.$type);
1604
1644
  const ref = node;
1605
1645
  const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
1606
1646
  if (isPrimitive(decl)) {
1607
- const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1608
- if (!ownerDecl?.node || ownerDecl.node.entity !== "type" && ownerDecl.node.entity !== "interface") {
1647
+ const ownerDecl$1 = this.doc.getDeclarationOwnerNode(ref.id);
1648
+ if (!ownerDecl$1?.node || ownerDecl$1.node.entity !== "type" && ownerDecl$1.node.entity !== "interface") {
1609
1649
  this.annotateType(decl, name);
1610
1650
  return this;
1611
1651
  }
@@ -1619,9 +1659,11 @@ else handle.prop(prop.id, propHandle.$type);
1619
1659
  }
1620
1660
  }
1621
1661
  const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
1622
- this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`);
1662
+ const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1663
+ const isImported = ownerDecl ? ownerDecl.doc !== this.doc : false;
1664
+ const refExpr = isImported ? `() => ${ref.id}` : ref.id;
1665
+ this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${refExpr}${chain})`);
1623
1666
  if (!ref.hasChain) {
1624
- const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1625
1667
  if (ownerDecl?.node) {
1626
1668
  const typeAnnotations = ownerDecl.doc.evalAnnotationsForNode(ownerDecl.node);
1627
1669
  typeAnnotations?.forEach((an) => {
package/dist/utils.cjs CHANGED
@@ -452,7 +452,8 @@ function createAnnotatedTypeNode(type, metadata, opts) {
452
452
  metadata,
453
453
  validator: validatorMethod,
454
454
  id: opts?.id,
455
- optional: opts?.optional
455
+ optional: opts?.optional,
456
+ ref: opts?.ref
456
457
  };
457
458
  }
458
459
  function isAnnotatedType(type) {
@@ -576,18 +577,57 @@ function defineAnnotatedType(_kind, base) {
576
577
  return this;
577
578
  },
578
579
  refTo(type$1, chain) {
579
- if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
580
- let newBase = type$1;
581
- const typeName = type$1.name || "Unknown";
582
- if (chain) for (let i = 0; i < chain.length; i++) {
583
- const c = chain[i];
584
- if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
580
+ if (isAnnotatedType(type$1)) {
581
+ let newBase = type$1;
582
+ const typeName = type$1.name || "Unknown";
583
+ if (chain) for (let i = 0; i < chain.length; i++) {
584
+ const c = chain[i];
585
+ if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
585
586
  else {
586
- const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
587
- throw new Error(`Can't find prop ${typeName}${keys}`);
587
+ const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
588
+ throw new Error(`Can't find prop ${typeName}${keys}`);
589
+ }
588
590
  }
589
- }
590
- this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
591
+ this.$type = createAnnotatedTypeNode(newBase.type, metadata, {
592
+ id: newBase.id,
593
+ ref: chain && chain.length > 0 ? {
594
+ type: () => type$1,
595
+ field: chain.join(".")
596
+ } : undefined
597
+ });
598
+ } else if (typeof type$1 === "function") {
599
+ const lazyType = type$1;
600
+ this.$type = createAnnotatedTypeNode({ kind: "" }, metadata, { ref: {
601
+ type: lazyType,
602
+ field: chain ? chain.join(".") : ""
603
+ } });
604
+ const node = this.$type;
605
+ const placeholder = node.type;
606
+ Object.defineProperty(node, "type", {
607
+ get() {
608
+ const t = lazyType();
609
+ if (!isAnnotatedType(t)) {
610
+ Object.defineProperty(node, "type", {
611
+ value: placeholder,
612
+ writable: false,
613
+ configurable: true
614
+ });
615
+ return placeholder;
616
+ }
617
+ let target = t;
618
+ if (chain) for (const c of chain) if (target.type.kind === "object" && target.type.props.has(c)) target = target.type.props.get(c);
619
+ else return t.type;
620
+ node.id = target.id || t.id;
621
+ Object.defineProperty(node, "type", {
622
+ value: target.type,
623
+ writable: false,
624
+ configurable: true
625
+ });
626
+ return target.type;
627
+ },
628
+ configurable: true
629
+ });
630
+ } else throw new Error(`${type$1} is not annotated type`);
591
631
  return this;
592
632
  },
593
633
  annotate(key, value, asArray) {
@@ -1020,6 +1060,7 @@ function throwFeatureDisabled(feature, option, annotation) {
1020
1060
  function flattenAnnotatedType(type, options) {
1021
1061
  const flatMap = new Map();
1022
1062
  const skipPhantom = !!options?.excludePhantomTypes;
1063
+ const visitedIds = new Set();
1023
1064
  function addFieldToFlatMap(name, def) {
1024
1065
  const existing = flatMap.get(name);
1025
1066
  if (existing) {
@@ -1056,6 +1097,13 @@ else flatUnion.item(existing);
1056
1097
  }
1057
1098
  }
1058
1099
  function flattenType(def, prefix = "", inComplexTypeOrArray = false) {
1100
+ const typeId = def.id;
1101
+ if (typeId && visitedIds.has(typeId)) {
1102
+ addFieldToFlatMap(prefix || "", def);
1103
+ if (prefix) options?.onField?.(prefix, def, def.metadata);
1104
+ return;
1105
+ }
1106
+ if (typeId) visitedIds.add(typeId);
1059
1107
  switch (def.type.kind) {
1060
1108
  case "object": {
1061
1109
  addFieldToFlatMap(prefix || "", def);
@@ -1102,20 +1150,31 @@ else flatUnion.item(existing);
1102
1150
  //#region packages/typescript/src/serialize.ts
1103
1151
  const SERIALIZE_VERSION = 1;
1104
1152
  function serializeAnnotatedType(type, options) {
1105
- const result = serializeNode(type, [], options);
1153
+ const visited = new Set();
1154
+ const result = serializeNode(type, [], options, visited);
1106
1155
  result.$v = SERIALIZE_VERSION;
1107
1156
  return result;
1108
1157
  }
1109
- function serializeNode(def, path, options) {
1158
+ function serializeNode(def, path, options, visited) {
1159
+ if (def.id && visited.has(def.id)) return {
1160
+ type: {
1161
+ kind: "$ref",
1162
+ id: def.id
1163
+ },
1164
+ metadata: {},
1165
+ ...def.optional ? { optional: true } : {},
1166
+ id: def.id
1167
+ };
1168
+ if (def.id) visited.add(def.id);
1110
1169
  const result = {
1111
- type: serializeTypeDef(def, path, options),
1170
+ type: serializeTypeDef(def, path, options, visited),
1112
1171
  metadata: serializeMetadata(def.metadata, path, def.type.kind, options)
1113
1172
  };
1114
1173
  if (def.optional) result.optional = true;
1115
1174
  if (def.id) result.id = def.id;
1116
1175
  return result;
1117
1176
  }
1118
- function serializeTypeDef(def, path, options) {
1177
+ function serializeTypeDef(def, path, options, visited) {
1119
1178
  return forAnnotatedType(def, {
1120
1179
  phantom(d) {
1121
1180
  return {
@@ -1135,13 +1194,13 @@ function serializeTypeDef(def, path, options) {
1135
1194
  },
1136
1195
  object(d) {
1137
1196
  const props = {};
1138
- for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options);
1197
+ for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options, visited);
1139
1198
  const propsPatterns = d.type.propsPatterns.map((pp) => ({
1140
1199
  pattern: {
1141
1200
  source: pp.pattern.source,
1142
1201
  flags: pp.pattern.flags
1143
1202
  },
1144
- def: serializeNode(pp.def, path, options)
1203
+ def: serializeNode(pp.def, path, options, visited)
1145
1204
  }));
1146
1205
  return {
1147
1206
  kind: "object",
@@ -1153,28 +1212,28 @@ function serializeTypeDef(def, path, options) {
1153
1212
  array(d) {
1154
1213
  return {
1155
1214
  kind: "array",
1156
- of: serializeNode(d.type.of, path, options),
1215
+ of: serializeNode(d.type.of, path, options, visited),
1157
1216
  tags: Array.from(d.type.tags)
1158
1217
  };
1159
1218
  },
1160
1219
  union(d) {
1161
1220
  return {
1162
1221
  kind: "union",
1163
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1222
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1164
1223
  tags: Array.from(d.type.tags)
1165
1224
  };
1166
1225
  },
1167
1226
  intersection(d) {
1168
1227
  return {
1169
1228
  kind: "intersection",
1170
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1229
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1171
1230
  tags: Array.from(d.type.tags)
1172
1231
  };
1173
1232
  },
1174
1233
  tuple(d) {
1175
1234
  return {
1176
1235
  kind: "tuple",
1177
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1236
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1178
1237
  tags: Array.from(d.type.tags)
1179
1238
  };
1180
1239
  }
@@ -1251,6 +1310,12 @@ function deserializeTypeDef(t) {
1251
1310
  items: t.items.map((item) => deserializeNode(item)),
1252
1311
  tags
1253
1312
  };
1313
+ case "$ref": return {
1314
+ kind: "object",
1315
+ props: new Map(),
1316
+ propsPatterns: [],
1317
+ tags
1318
+ };
1254
1319
  default: throw new Error(`Unknown serialized type kind "${t.kind}"`);
1255
1320
  }
1256
1321
  }
package/dist/utils.d.ts CHANGED
@@ -103,6 +103,10 @@ declare class ValidatorError extends Error {
103
103
  declare function createAnnotatedTypeNode(type: TAtscriptTypeDef, metadata: TMetadataMap<AtscriptMetadata>, opts?: {
104
104
  id?: string;
105
105
  optional?: boolean;
106
+ ref?: {
107
+ type: () => TAtscriptAnnotatedType;
108
+ field: string;
109
+ };
106
110
  }): TAtscriptAnnotatedType;
107
111
  /** Type definition for union, intersection, or tuple types. */
108
112
  interface TAtscriptTypeComplex<DataType = unknown> {
@@ -191,6 +195,10 @@ interface TAtscriptAnnotatedType<T extends TAtscriptTypeDef = TAtscriptTypeDef,
191
195
  metadata: TMetadataMap<AtscriptMetadata>;
192
196
  optional?: boolean;
193
197
  id?: string;
198
+ ref?: {
199
+ type: () => TAtscriptAnnotatedType;
200
+ field: string;
201
+ };
194
202
  }
195
203
  /** An annotated type that is also a class constructor (i.e. a generated interface class). */
196
204
  type TAtscriptAnnotatedTypeConstructor = TAtscriptAnnotatedType & (new (...args: any[]) => any);
@@ -252,9 +260,11 @@ interface TAnnotatedTypeHandle {
252
260
  propPattern(pattern: RegExp, value: TAtscriptAnnotatedType): TAnnotatedTypeHandle;
253
261
  optional(value?: boolean): TAnnotatedTypeHandle;
254
262
  copyMetadata(fromMetadata: TMetadataMap<AtscriptMetadata>): TAnnotatedTypeHandle;
255
- refTo(type: TAtscriptAnnotatedType & {
263
+ refTo(type: (TAtscriptAnnotatedType & {
256
264
  name?: string;
257
- }, chain?: string[]): TAnnotatedTypeHandle;
265
+ }) | (() => TAtscriptAnnotatedType & {
266
+ name?: string;
267
+ }), chain?: string[]): TAnnotatedTypeHandle;
258
268
  annotate(key: keyof AtscriptMetadata, value: any, asArray?: boolean): TAnnotatedTypeHandle;
259
269
  id(value: string): TAnnotatedTypeHandle;
260
270
  }
@@ -478,7 +488,11 @@ interface TSerializedTypeComplex {
478
488
  items: TSerializedAnnotatedTypeInner[];
479
489
  tags: string[];
480
490
  }
481
- type TSerializedTypeDef = TSerializedTypeFinal | TSerializedTypeObject | TSerializedTypeArray | TSerializedTypeComplex;
491
+ interface TSerializedTypeRef {
492
+ kind: '$ref';
493
+ id: string;
494
+ }
495
+ type TSerializedTypeDef = TSerializedTypeFinal | TSerializedTypeObject | TSerializedTypeArray | TSerializedTypeComplex | TSerializedTypeRef;
482
496
  /** Context passed to {@link TSerializeOptions.processAnnotation} for each annotation entry. */
483
497
  interface TProcessAnnotationContext {
484
498
  /** Annotation key, e.g. "meta.label" */
package/dist/utils.mjs CHANGED
@@ -451,7 +451,8 @@ function createAnnotatedTypeNode(type, metadata, opts) {
451
451
  metadata,
452
452
  validator: validatorMethod,
453
453
  id: opts?.id,
454
- optional: opts?.optional
454
+ optional: opts?.optional,
455
+ ref: opts?.ref
455
456
  };
456
457
  }
457
458
  function isAnnotatedType(type) {
@@ -575,18 +576,57 @@ function defineAnnotatedType(_kind, base) {
575
576
  return this;
576
577
  },
577
578
  refTo(type$1, chain) {
578
- if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
579
- let newBase = type$1;
580
- const typeName = type$1.name || "Unknown";
581
- if (chain) for (let i = 0; i < chain.length; i++) {
582
- const c = chain[i];
583
- if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
579
+ if (isAnnotatedType(type$1)) {
580
+ let newBase = type$1;
581
+ const typeName = type$1.name || "Unknown";
582
+ if (chain) for (let i = 0; i < chain.length; i++) {
583
+ const c = chain[i];
584
+ if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
584
585
  else {
585
- const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
586
- throw new Error(`Can't find prop ${typeName}${keys}`);
586
+ const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
587
+ throw new Error(`Can't find prop ${typeName}${keys}`);
588
+ }
587
589
  }
588
- }
589
- this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
590
+ this.$type = createAnnotatedTypeNode(newBase.type, metadata, {
591
+ id: newBase.id,
592
+ ref: chain && chain.length > 0 ? {
593
+ type: () => type$1,
594
+ field: chain.join(".")
595
+ } : undefined
596
+ });
597
+ } else if (typeof type$1 === "function") {
598
+ const lazyType = type$1;
599
+ this.$type = createAnnotatedTypeNode({ kind: "" }, metadata, { ref: {
600
+ type: lazyType,
601
+ field: chain ? chain.join(".") : ""
602
+ } });
603
+ const node = this.$type;
604
+ const placeholder = node.type;
605
+ Object.defineProperty(node, "type", {
606
+ get() {
607
+ const t = lazyType();
608
+ if (!isAnnotatedType(t)) {
609
+ Object.defineProperty(node, "type", {
610
+ value: placeholder,
611
+ writable: false,
612
+ configurable: true
613
+ });
614
+ return placeholder;
615
+ }
616
+ let target = t;
617
+ if (chain) for (const c of chain) if (target.type.kind === "object" && target.type.props.has(c)) target = target.type.props.get(c);
618
+ else return t.type;
619
+ node.id = target.id || t.id;
620
+ Object.defineProperty(node, "type", {
621
+ value: target.type,
622
+ writable: false,
623
+ configurable: true
624
+ });
625
+ return target.type;
626
+ },
627
+ configurable: true
628
+ });
629
+ } else throw new Error(`${type$1} is not annotated type`);
590
630
  return this;
591
631
  },
592
632
  annotate(key, value, asArray) {
@@ -1019,6 +1059,7 @@ function throwFeatureDisabled(feature, option, annotation) {
1019
1059
  function flattenAnnotatedType(type, options) {
1020
1060
  const flatMap = new Map();
1021
1061
  const skipPhantom = !!options?.excludePhantomTypes;
1062
+ const visitedIds = new Set();
1022
1063
  function addFieldToFlatMap(name, def) {
1023
1064
  const existing = flatMap.get(name);
1024
1065
  if (existing) {
@@ -1055,6 +1096,13 @@ else flatUnion.item(existing);
1055
1096
  }
1056
1097
  }
1057
1098
  function flattenType(def, prefix = "", inComplexTypeOrArray = false) {
1099
+ const typeId = def.id;
1100
+ if (typeId && visitedIds.has(typeId)) {
1101
+ addFieldToFlatMap(prefix || "", def);
1102
+ if (prefix) options?.onField?.(prefix, def, def.metadata);
1103
+ return;
1104
+ }
1105
+ if (typeId) visitedIds.add(typeId);
1058
1106
  switch (def.type.kind) {
1059
1107
  case "object": {
1060
1108
  addFieldToFlatMap(prefix || "", def);
@@ -1101,20 +1149,31 @@ else flatUnion.item(existing);
1101
1149
  //#region packages/typescript/src/serialize.ts
1102
1150
  const SERIALIZE_VERSION = 1;
1103
1151
  function serializeAnnotatedType(type, options) {
1104
- const result = serializeNode(type, [], options);
1152
+ const visited = new Set();
1153
+ const result = serializeNode(type, [], options, visited);
1105
1154
  result.$v = SERIALIZE_VERSION;
1106
1155
  return result;
1107
1156
  }
1108
- function serializeNode(def, path, options) {
1157
+ function serializeNode(def, path, options, visited) {
1158
+ if (def.id && visited.has(def.id)) return {
1159
+ type: {
1160
+ kind: "$ref",
1161
+ id: def.id
1162
+ },
1163
+ metadata: {},
1164
+ ...def.optional ? { optional: true } : {},
1165
+ id: def.id
1166
+ };
1167
+ if (def.id) visited.add(def.id);
1109
1168
  const result = {
1110
- type: serializeTypeDef(def, path, options),
1169
+ type: serializeTypeDef(def, path, options, visited),
1111
1170
  metadata: serializeMetadata(def.metadata, path, def.type.kind, options)
1112
1171
  };
1113
1172
  if (def.optional) result.optional = true;
1114
1173
  if (def.id) result.id = def.id;
1115
1174
  return result;
1116
1175
  }
1117
- function serializeTypeDef(def, path, options) {
1176
+ function serializeTypeDef(def, path, options, visited) {
1118
1177
  return forAnnotatedType(def, {
1119
1178
  phantom(d) {
1120
1179
  return {
@@ -1134,13 +1193,13 @@ function serializeTypeDef(def, path, options) {
1134
1193
  },
1135
1194
  object(d) {
1136
1195
  const props = {};
1137
- for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options);
1196
+ for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options, visited);
1138
1197
  const propsPatterns = d.type.propsPatterns.map((pp) => ({
1139
1198
  pattern: {
1140
1199
  source: pp.pattern.source,
1141
1200
  flags: pp.pattern.flags
1142
1201
  },
1143
- def: serializeNode(pp.def, path, options)
1202
+ def: serializeNode(pp.def, path, options, visited)
1144
1203
  }));
1145
1204
  return {
1146
1205
  kind: "object",
@@ -1152,28 +1211,28 @@ function serializeTypeDef(def, path, options) {
1152
1211
  array(d) {
1153
1212
  return {
1154
1213
  kind: "array",
1155
- of: serializeNode(d.type.of, path, options),
1214
+ of: serializeNode(d.type.of, path, options, visited),
1156
1215
  tags: Array.from(d.type.tags)
1157
1216
  };
1158
1217
  },
1159
1218
  union(d) {
1160
1219
  return {
1161
1220
  kind: "union",
1162
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1221
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1163
1222
  tags: Array.from(d.type.tags)
1164
1223
  };
1165
1224
  },
1166
1225
  intersection(d) {
1167
1226
  return {
1168
1227
  kind: "intersection",
1169
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1228
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1170
1229
  tags: Array.from(d.type.tags)
1171
1230
  };
1172
1231
  },
1173
1232
  tuple(d) {
1174
1233
  return {
1175
1234
  kind: "tuple",
1176
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1235
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1177
1236
  tags: Array.from(d.type.tags)
1178
1237
  };
1179
1238
  }
@@ -1250,6 +1309,12 @@ function deserializeTypeDef(t) {
1250
1309
  items: t.items.map((item) => deserializeNode(item)),
1251
1310
  tags
1252
1311
  };
1312
+ case "$ref": return {
1313
+ kind: "object",
1314
+ props: new Map(),
1315
+ propsPatterns: [],
1316
+ tags
1317
+ };
1253
1318
  default: throw new Error(`Unknown serialized type kind "${t.kind}"`);
1254
1319
  }
1255
1320
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/typescript",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
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.33"
67
+ "@atscript/core": "^0.1.34"
68
68
  },
69
69
  "build": [
70
70
  {},