@atscript/typescript 0.1.32 → 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
@@ -482,28 +482,72 @@ else {
482
482
  *
483
483
  * - **Single PK** (one `@meta.id`) → `static __pk: <scalar type>`
484
484
  * - **Compound PK** (multiple `@meta.id`) → `static __pk: { field1: Type1; field2: Type2 }`
485
- * - **No PK** → no `__pk` emitted
485
+ * - **No PK** → no `__pk` emitted (unless unique indexes exist)
486
+ * - **Unique indexes** (`@db.index.unique`) → appended as union members
487
+ * - **Mongo collection** → always includes `string` (ObjectId) in the union;
488
+ * if no `@meta.id` fields, `__pk` is just `string`
486
489
  */ renderPk(node) {
490
+ const isMongoCollection = !!node.annotations?.some((a) => a.name === "db.mongo.collection");
487
491
  let struct;
488
492
  if (node.hasExtends) struct = this.doc.resolveInterfaceExtends(node);
489
493
  if (!struct) struct = node.getDefinition();
490
494
  if (!struct || !(0, __atscript_core.isStructure)(struct)) return;
495
+ const structNode = struct;
491
496
  const pkProps = [];
492
- for (const [name, prop] of struct.props) {
497
+ const uniqueByIndex = new Map();
498
+ for (const [name, prop] of structNode.props) {
493
499
  if (prop.token("identifier")?.pattern) continue;
500
+ if (isMongoCollection && name === "_id") continue;
494
501
  if (prop.countAnnotations("meta.id") > 0) pkProps.push({
495
502
  name,
496
503
  prop
497
504
  });
505
+ if (prop.annotations) {
506
+ for (const ann of prop.annotations) if (ann.name === "db.index.unique") {
507
+ const indexName = ann.args[0]?.text ?? name;
508
+ let group = uniqueByIndex.get(indexName);
509
+ if (!group) {
510
+ group = [];
511
+ uniqueByIndex.set(indexName, group);
512
+ }
513
+ group.push({
514
+ name,
515
+ prop
516
+ });
517
+ }
518
+ }
519
+ }
520
+ const uniqueProps = [];
521
+ const pkNames = new Set(pkProps.map((p) => p.name));
522
+ for (const fields of uniqueByIndex.values()) if (fields.length === 1 && !pkNames.has(fields[0].name)) uniqueProps.push(fields[0]);
523
+ if (pkProps.length === 0 && uniqueProps.length === 0 && !isMongoCollection) return;
524
+ let mongoIdType;
525
+ if (isMongoCollection) {
526
+ const idProp = structNode.props.get("_id");
527
+ if (idProp) mongoIdType = this.renderTypeDefString(idProp.getDefinition()).trim();
528
+ mongoIdType ?? (mongoIdType = "string");
529
+ }
530
+ const uniqueTypes = [];
531
+ const seenTypes = new Set();
532
+ for (const { prop } of uniqueProps) {
533
+ const rendered = this.renderTypeDefString(prop.getDefinition()).trim();
534
+ if (!seenTypes.has(rendered)) {
535
+ seenTypes.add(rendered);
536
+ uniqueTypes.push(rendered);
537
+ }
498
538
  }
499
- if (pkProps.length === 0) return;
500
539
  this.writeln();
501
- if (pkProps.length === 1) {
540
+ const uniqueSuffix = uniqueTypes.length > 0 ? ` | ${uniqueTypes.join(" | ")}` : "";
541
+ if (pkProps.length === 0 && !isMongoCollection) this.writeln(`static __pk: ${uniqueTypes.join(" | ")}`);
542
+ else if (pkProps.length === 0) this.writeln(`static __pk: ${mongoIdType}${uniqueSuffix}`);
543
+ else if (pkProps.length === 1) {
502
544
  this.write("static __pk: ");
503
- const renderedDef = this.renderTypeDefString(pkProps[0].prop.getDefinition());
504
- renderedDef.split("\n").forEach((l) => this.writeln(l));
545
+ if (isMongoCollection) this.write(`${mongoIdType} | `);
546
+ const renderedDef = this.renderTypeDefString(pkProps[0].prop.getDefinition()).trim();
547
+ this.writeln(`${renderedDef}${uniqueSuffix}`);
505
548
  } else {
506
549
  this.write("static __pk: ");
550
+ if (isMongoCollection) this.write(`${mongoIdType} | `);
507
551
  this.blockln("{}");
508
552
  for (const { name, prop } of pkProps) {
509
553
  this.write(wrapProp(name), ": ");
@@ -511,6 +555,7 @@ else {
511
555
  renderedDef.split("\n").forEach((l) => this.writeln(l));
512
556
  }
513
557
  this.pop();
558
+ this.writeln(uniqueSuffix);
514
559
  }
515
560
  }
516
561
  phantomPropType(def) {
@@ -998,6 +1043,25 @@ var ValidatorError = class extends Error {
998
1043
 
999
1044
  //#endregion
1000
1045
  //#region packages/typescript/src/annotated-type.ts
1046
+ const COMPLEX_KINDS = new Set([
1047
+ "union",
1048
+ "intersection",
1049
+ "tuple"
1050
+ ]);
1051
+ /** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
1052
+ return new Validator(this, opts);
1053
+ }
1054
+ function createAnnotatedTypeNode(type, metadata, opts) {
1055
+ return {
1056
+ __is_atscript_annotated_type: true,
1057
+ type,
1058
+ metadata,
1059
+ validator: validatorMethod,
1060
+ id: opts?.id,
1061
+ optional: opts?.optional,
1062
+ ref: opts?.ref
1063
+ };
1064
+ }
1001
1065
  function isAnnotatedType(type) {
1002
1066
  return type && type.__is_atscript_annotated_type;
1003
1067
  }
@@ -1014,38 +1078,24 @@ function defineAnnotatedType(_kind, base) {
1014
1078
  const kind = _kind || "";
1015
1079
  const type = base?.type || {};
1016
1080
  type.kind = kind;
1017
- if ([
1018
- "union",
1019
- "intersection",
1020
- "tuple"
1021
- ].includes(kind)) type.items = [];
1081
+ if (COMPLEX_KINDS.has(kind)) type.items = [];
1022
1082
  if (kind === "object") {
1023
1083
  type.props = new Map();
1024
1084
  type.propsPatterns = [];
1025
1085
  }
1026
1086
  type.tags = new Set();
1027
1087
  const metadata = base?.metadata || new Map();
1028
- if (base) Object.assign(base, {
1029
- __is_atscript_annotated_type: true,
1030
- metadata,
1031
- type,
1032
- validator(opts) {
1033
- return new Validator(this, opts);
1034
- }
1035
- });
1036
- else base = {
1088
+ const payload = {
1037
1089
  __is_atscript_annotated_type: true,
1038
1090
  metadata,
1039
1091
  type,
1040
- validator(opts) {
1041
- return new Validator(this, opts);
1042
- }
1092
+ validator: validatorMethod
1043
1093
  };
1094
+ base = base ? Object.assign(base, payload) : payload;
1044
1095
  const handle = {
1045
1096
  $type: base,
1046
1097
  $def: type,
1047
1098
  $metadata: metadata,
1048
- _existingObject: undefined,
1049
1099
  tags(...tags) {
1050
1100
  for (const tag of tags) this.$def.tags.add(tag);
1051
1101
  return this;
@@ -1086,26 +1136,56 @@ else base = {
1086
1136
  return this;
1087
1137
  },
1088
1138
  refTo(type$1, chain) {
1089
- let newBase = type$1;
1090
- const typeName = type$1.name || "Unknown";
1091
- if (isAnnotatedType(newBase)) {
1092
- let keys = "";
1093
- for (const c of chain || []) {
1094
- keys += `["${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];
1095
1144
  if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
1096
- else throw new Error(`Can't find prop ${typeName}${keys}`);
1145
+ else {
1146
+ const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
1147
+ throw new Error(`Can't find prop ${typeName}${keys}`);
1148
+ }
1097
1149
  }
1098
- if (!newBase && keys) throw new Error(`Can't find prop ${typeName}${keys}`);
1099
- else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
1100
- this.$type = {
1101
- __is_atscript_annotated_type: true,
1102
- type: newBase.type,
1103
- metadata,
1150
+ this.$type = createAnnotatedTypeNode(newBase.type, metadata, {
1104
1151
  id: newBase.id,
1105
- validator(opts) {
1106
- return new Validator(this, opts);
1107
- }
1108
- };
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
+ });
1109
1189
  } else throw new Error(`${type$1} is not annotated type`);
1110
1190
  return this;
1111
1191
  },
@@ -1156,10 +1236,10 @@ function forAnnotatedType(def, handlers) {
1156
1236
  const firstObj = items[0].type;
1157
1237
  const candidates = [];
1158
1238
  for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
1159
- const validCandidates = [];
1239
+ let result = null;
1160
1240
  for (const candidate of candidates) {
1161
1241
  const values = new Set();
1162
- const mapping = {};
1242
+ const indexMapping = {};
1163
1243
  let valid = true;
1164
1244
  for (let i = 0; i < items.length; i++) {
1165
1245
  const obj = items[i].type;
@@ -1174,19 +1254,21 @@ function forAnnotatedType(def, handlers) {
1174
1254
  break;
1175
1255
  }
1176
1256
  values.add(val);
1177
- mapping[String(val)] = `#/oneOf/${i}`;
1257
+ indexMapping[String(val)] = i;
1258
+ }
1259
+ if (valid) {
1260
+ if (result) return null;
1261
+ result = {
1262
+ propertyName: candidate,
1263
+ indexMapping
1264
+ };
1178
1265
  }
1179
- if (valid) validCandidates.push({
1180
- propertyName: candidate,
1181
- mapping
1182
- });
1183
1266
  }
1184
- if (validCandidates.length === 1) return validCandidates[0];
1185
- return null;
1267
+ return result;
1186
1268
  }
1187
1269
  function buildJsonSchema(type) {
1188
1270
  const defs = {};
1189
- let isRoot = true;
1271
+ let hasDefs = false;
1190
1272
  const buildObject = (d) => {
1191
1273
  const properties = {};
1192
1274
  const required = [];
@@ -1203,15 +1285,15 @@ function buildJsonSchema(type) {
1203
1285
  return schema$1;
1204
1286
  };
1205
1287
  const build$1 = (def) => {
1206
- if (def.id && def.type.kind === "object" && !isRoot) {
1288
+ if (def.id && def.type.kind === "object" && def !== type) {
1207
1289
  const name = def.id;
1208
1290
  if (!defs[name]) {
1291
+ hasDefs = true;
1209
1292
  defs[name] = {};
1210
1293
  defs[name] = buildObject(def);
1211
1294
  }
1212
1295
  return { $ref: `#/$defs/${name}` };
1213
1296
  }
1214
- isRoot = false;
1215
1297
  const meta = def.metadata;
1216
1298
  return forAnnotatedType(def, {
1217
1299
  phantom() {
@@ -1236,11 +1318,9 @@ function buildJsonSchema(type) {
1236
1318
  if (disc) {
1237
1319
  const oneOf = d.type.items.map(build$1);
1238
1320
  const mapping = {};
1239
- for (const [val, origPath] of Object.entries(disc.mapping)) {
1240
- const idx = Number.parseInt(origPath.split("/").pop(), 10);
1321
+ for (const [val, idx] of Object.entries(disc.indexMapping)) {
1241
1322
  const item = d.type.items[idx];
1242
- if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
1243
- else mapping[val] = origPath;
1323
+ mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
1244
1324
  }
1245
1325
  return {
1246
1326
  oneOf,
@@ -1290,7 +1370,7 @@ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ patte
1290
1370
  });
1291
1371
  };
1292
1372
  const schema = build$1(type);
1293
- if (Object.keys(defs).length > 0) return {
1373
+ if (hasDefs) return {
1294
1374
  ...schema,
1295
1375
  $defs: defs
1296
1376
  };
@@ -1591,8 +1671,8 @@ else handle.prop(prop.id, propHandle.$type);
1591
1671
  const ref = node;
1592
1672
  const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
1593
1673
  if ((0, __atscript_core.isPrimitive)(decl)) {
1594
- const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1595
- 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") {
1596
1676
  this.annotateType(decl, name);
1597
1677
  return this;
1598
1678
  }
@@ -1606,9 +1686,11 @@ else handle.prop(prop.id, propHandle.$type);
1606
1686
  }
1607
1687
  }
1608
1688
  const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
1609
- 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})`);
1610
1693
  if (!ref.hasChain) {
1611
- const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1612
1694
  if (ownerDecl?.node) {
1613
1695
  const typeAnnotations = ownerDecl.doc.evalAnnotationsForNode(ownerDecl.node);
1614
1696
  typeAnnotations?.forEach((an) => {
package/dist/index.cjs CHANGED
@@ -480,28 +480,72 @@ else {
480
480
  *
481
481
  * - **Single PK** (one `@meta.id`) → `static __pk: <scalar type>`
482
482
  * - **Compound PK** (multiple `@meta.id`) → `static __pk: { field1: Type1; field2: Type2 }`
483
- * - **No PK** → no `__pk` emitted
483
+ * - **No PK** → no `__pk` emitted (unless unique indexes exist)
484
+ * - **Unique indexes** (`@db.index.unique`) → appended as union members
485
+ * - **Mongo collection** → always includes `string` (ObjectId) in the union;
486
+ * if no `@meta.id` fields, `__pk` is just `string`
484
487
  */ renderPk(node) {
488
+ const isMongoCollection = !!node.annotations?.some((a) => a.name === "db.mongo.collection");
485
489
  let struct;
486
490
  if (node.hasExtends) struct = this.doc.resolveInterfaceExtends(node);
487
491
  if (!struct) struct = node.getDefinition();
488
492
  if (!struct || !(0, __atscript_core.isStructure)(struct)) return;
493
+ const structNode = struct;
489
494
  const pkProps = [];
490
- for (const [name, prop] of struct.props) {
495
+ const uniqueByIndex = new Map();
496
+ for (const [name, prop] of structNode.props) {
491
497
  if (prop.token("identifier")?.pattern) continue;
498
+ if (isMongoCollection && name === "_id") continue;
492
499
  if (prop.countAnnotations("meta.id") > 0) pkProps.push({
493
500
  name,
494
501
  prop
495
502
  });
503
+ if (prop.annotations) {
504
+ for (const ann of prop.annotations) if (ann.name === "db.index.unique") {
505
+ const indexName = ann.args[0]?.text ?? name;
506
+ let group = uniqueByIndex.get(indexName);
507
+ if (!group) {
508
+ group = [];
509
+ uniqueByIndex.set(indexName, group);
510
+ }
511
+ group.push({
512
+ name,
513
+ prop
514
+ });
515
+ }
516
+ }
517
+ }
518
+ const uniqueProps = [];
519
+ const pkNames = new Set(pkProps.map((p) => p.name));
520
+ for (const fields of uniqueByIndex.values()) if (fields.length === 1 && !pkNames.has(fields[0].name)) uniqueProps.push(fields[0]);
521
+ if (pkProps.length === 0 && uniqueProps.length === 0 && !isMongoCollection) return;
522
+ let mongoIdType;
523
+ if (isMongoCollection) {
524
+ const idProp = structNode.props.get("_id");
525
+ if (idProp) mongoIdType = this.renderTypeDefString(idProp.getDefinition()).trim();
526
+ mongoIdType ?? (mongoIdType = "string");
527
+ }
528
+ const uniqueTypes = [];
529
+ const seenTypes = new Set();
530
+ for (const { prop } of uniqueProps) {
531
+ const rendered = this.renderTypeDefString(prop.getDefinition()).trim();
532
+ if (!seenTypes.has(rendered)) {
533
+ seenTypes.add(rendered);
534
+ uniqueTypes.push(rendered);
535
+ }
496
536
  }
497
- if (pkProps.length === 0) return;
498
537
  this.writeln();
499
- if (pkProps.length === 1) {
538
+ const uniqueSuffix = uniqueTypes.length > 0 ? ` | ${uniqueTypes.join(" | ")}` : "";
539
+ if (pkProps.length === 0 && !isMongoCollection) this.writeln(`static __pk: ${uniqueTypes.join(" | ")}`);
540
+ else if (pkProps.length === 0) this.writeln(`static __pk: ${mongoIdType}${uniqueSuffix}`);
541
+ else if (pkProps.length === 1) {
500
542
  this.write("static __pk: ");
501
- const renderedDef = this.renderTypeDefString(pkProps[0].prop.getDefinition());
502
- renderedDef.split("\n").forEach((l) => this.writeln(l));
543
+ if (isMongoCollection) this.write(`${mongoIdType} | `);
544
+ const renderedDef = this.renderTypeDefString(pkProps[0].prop.getDefinition()).trim();
545
+ this.writeln(`${renderedDef}${uniqueSuffix}`);
503
546
  } else {
504
547
  this.write("static __pk: ");
548
+ if (isMongoCollection) this.write(`${mongoIdType} | `);
505
549
  this.blockln("{}");
506
550
  for (const { name, prop } of pkProps) {
507
551
  this.write(wrapProp(name), ": ");
@@ -509,6 +553,7 @@ else {
509
553
  renderedDef.split("\n").forEach((l) => this.writeln(l));
510
554
  }
511
555
  this.pop();
556
+ this.writeln(uniqueSuffix);
512
557
  }
513
558
  }
514
559
  phantomPropType(def) {
@@ -996,6 +1041,25 @@ var ValidatorError = class extends Error {
996
1041
 
997
1042
  //#endregion
998
1043
  //#region packages/typescript/src/annotated-type.ts
1044
+ const COMPLEX_KINDS = new Set([
1045
+ "union",
1046
+ "intersection",
1047
+ "tuple"
1048
+ ]);
1049
+ /** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
1050
+ return new Validator(this, opts);
1051
+ }
1052
+ function createAnnotatedTypeNode(type, metadata, opts) {
1053
+ return {
1054
+ __is_atscript_annotated_type: true,
1055
+ type,
1056
+ metadata,
1057
+ validator: validatorMethod,
1058
+ id: opts?.id,
1059
+ optional: opts?.optional,
1060
+ ref: opts?.ref
1061
+ };
1062
+ }
999
1063
  function isAnnotatedType(type) {
1000
1064
  return type && type.__is_atscript_annotated_type;
1001
1065
  }
@@ -1012,38 +1076,24 @@ function defineAnnotatedType(_kind, base) {
1012
1076
  const kind = _kind || "";
1013
1077
  const type = base?.type || {};
1014
1078
  type.kind = kind;
1015
- if ([
1016
- "union",
1017
- "intersection",
1018
- "tuple"
1019
- ].includes(kind)) type.items = [];
1079
+ if (COMPLEX_KINDS.has(kind)) type.items = [];
1020
1080
  if (kind === "object") {
1021
1081
  type.props = new Map();
1022
1082
  type.propsPatterns = [];
1023
1083
  }
1024
1084
  type.tags = new Set();
1025
1085
  const metadata = base?.metadata || new Map();
1026
- if (base) Object.assign(base, {
1086
+ const payload = {
1027
1087
  __is_atscript_annotated_type: true,
1028
1088
  metadata,
1029
1089
  type,
1030
- validator(opts) {
1031
- return new Validator(this, opts);
1032
- }
1033
- });
1034
- else base = {
1035
- __is_atscript_annotated_type: true,
1036
- metadata,
1037
- type,
1038
- validator(opts) {
1039
- return new Validator(this, opts);
1040
- }
1090
+ validator: validatorMethod
1041
1091
  };
1092
+ base = base ? Object.assign(base, payload) : payload;
1042
1093
  const handle = {
1043
1094
  $type: base,
1044
1095
  $def: type,
1045
1096
  $metadata: metadata,
1046
- _existingObject: undefined,
1047
1097
  tags(...tags) {
1048
1098
  for (const tag of tags) this.$def.tags.add(tag);
1049
1099
  return this;
@@ -1084,26 +1134,56 @@ else base = {
1084
1134
  return this;
1085
1135
  },
1086
1136
  refTo(type$1, chain) {
1087
- let newBase = type$1;
1088
- const typeName = type$1.name || "Unknown";
1089
- if (isAnnotatedType(newBase)) {
1090
- let keys = "";
1091
- for (const c of chain || []) {
1092
- keys += `["${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];
1093
1142
  if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
1094
- else throw new Error(`Can't find prop ${typeName}${keys}`);
1143
+ else {
1144
+ const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
1145
+ throw new Error(`Can't find prop ${typeName}${keys}`);
1146
+ }
1095
1147
  }
1096
- if (!newBase && keys) throw new Error(`Can't find prop ${typeName}${keys}`);
1097
- else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
1098
- this.$type = {
1099
- __is_atscript_annotated_type: true,
1100
- type: newBase.type,
1101
- metadata,
1148
+ this.$type = createAnnotatedTypeNode(newBase.type, metadata, {
1102
1149
  id: newBase.id,
1103
- validator(opts) {
1104
- return new Validator(this, opts);
1105
- }
1106
- };
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
+ });
1107
1187
  } else throw new Error(`${type$1} is not annotated type`);
1108
1188
  return this;
1109
1189
  },
@@ -1154,10 +1234,10 @@ function forAnnotatedType(def, handlers) {
1154
1234
  const firstObj = items[0].type;
1155
1235
  const candidates = [];
1156
1236
  for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
1157
- const validCandidates = [];
1237
+ let result = null;
1158
1238
  for (const candidate of candidates) {
1159
1239
  const values = new Set();
1160
- const mapping = {};
1240
+ const indexMapping = {};
1161
1241
  let valid = true;
1162
1242
  for (let i = 0; i < items.length; i++) {
1163
1243
  const obj = items[i].type;
@@ -1172,19 +1252,21 @@ function forAnnotatedType(def, handlers) {
1172
1252
  break;
1173
1253
  }
1174
1254
  values.add(val);
1175
- mapping[String(val)] = `#/oneOf/${i}`;
1255
+ indexMapping[String(val)] = i;
1256
+ }
1257
+ if (valid) {
1258
+ if (result) return null;
1259
+ result = {
1260
+ propertyName: candidate,
1261
+ indexMapping
1262
+ };
1176
1263
  }
1177
- if (valid) validCandidates.push({
1178
- propertyName: candidate,
1179
- mapping
1180
- });
1181
1264
  }
1182
- if (validCandidates.length === 1) return validCandidates[0];
1183
- return null;
1265
+ return result;
1184
1266
  }
1185
1267
  function buildJsonSchema(type) {
1186
1268
  const defs = {};
1187
- let isRoot = true;
1269
+ let hasDefs = false;
1188
1270
  const buildObject = (d) => {
1189
1271
  const properties = {};
1190
1272
  const required = [];
@@ -1201,15 +1283,15 @@ function buildJsonSchema(type) {
1201
1283
  return schema$1;
1202
1284
  };
1203
1285
  const build = (def) => {
1204
- if (def.id && def.type.kind === "object" && !isRoot) {
1286
+ if (def.id && def.type.kind === "object" && def !== type) {
1205
1287
  const name = def.id;
1206
1288
  if (!defs[name]) {
1289
+ hasDefs = true;
1207
1290
  defs[name] = {};
1208
1291
  defs[name] = buildObject(def);
1209
1292
  }
1210
1293
  return { $ref: `#/$defs/${name}` };
1211
1294
  }
1212
- isRoot = false;
1213
1295
  const meta = def.metadata;
1214
1296
  return forAnnotatedType(def, {
1215
1297
  phantom() {
@@ -1234,11 +1316,9 @@ function buildJsonSchema(type) {
1234
1316
  if (disc) {
1235
1317
  const oneOf = d.type.items.map(build);
1236
1318
  const mapping = {};
1237
- for (const [val, origPath] of Object.entries(disc.mapping)) {
1238
- const idx = Number.parseInt(origPath.split("/").pop(), 10);
1319
+ for (const [val, idx] of Object.entries(disc.indexMapping)) {
1239
1320
  const item = d.type.items[idx];
1240
- if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
1241
- else mapping[val] = origPath;
1321
+ mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
1242
1322
  }
1243
1323
  return {
1244
1324
  oneOf,
@@ -1288,7 +1368,7 @@ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ patte
1288
1368
  });
1289
1369
  };
1290
1370
  const schema = build(type);
1291
- if (Object.keys(defs).length > 0) return {
1371
+ if (hasDefs) return {
1292
1372
  ...schema,
1293
1373
  $defs: defs
1294
1374
  };
@@ -1589,8 +1669,8 @@ else handle.prop(prop.id, propHandle.$type);
1589
1669
  const ref = node;
1590
1670
  const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
1591
1671
  if ((0, __atscript_core.isPrimitive)(decl)) {
1592
- const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1593
- 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") {
1594
1674
  this.annotateType(decl, name);
1595
1675
  return this;
1596
1676
  }
@@ -1604,9 +1684,11 @@ else handle.prop(prop.id, propHandle.$type);
1604
1684
  }
1605
1685
  }
1606
1686
  const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
1607
- 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})`);
1608
1691
  if (!ref.hasChain) {
1609
- const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
1610
1692
  if (ownerDecl?.node) {
1611
1693
  const typeAnnotations = ownerDecl.doc.evalAnnotationsForNode(ownerDecl.node);
1612
1694
  typeAnnotations?.forEach((an) => {