@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 +144 -62
- package/dist/index.cjs +144 -62
- package/dist/index.mjs +144 -62
- package/dist/utils.cjs +141 -97
- package/dist/utils.d.ts +26 -5
- package/dist/utils.mjs +141 -98
- package/package.json +2 -2
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
504
|
-
renderedDef.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
let
|
|
1093
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1106
|
-
|
|
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
|
-
|
|
1239
|
+
let result = null;
|
|
1160
1240
|
for (const candidate of candidates) {
|
|
1161
1241
|
const values = new Set();
|
|
1162
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
1185
|
-
return null;
|
|
1267
|
+
return result;
|
|
1186
1268
|
}
|
|
1187
1269
|
function buildJsonSchema(type) {
|
|
1188
1270
|
const defs = {};
|
|
1189
|
-
let
|
|
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" &&
|
|
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,
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
502
|
-
renderedDef.
|
|
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
|
-
|
|
1086
|
+
const payload = {
|
|
1027
1087
|
__is_atscript_annotated_type: true,
|
|
1028
1088
|
metadata,
|
|
1029
1089
|
type,
|
|
1030
|
-
validator
|
|
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
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
let
|
|
1091
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1104
|
-
|
|
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
|
-
|
|
1237
|
+
let result = null;
|
|
1158
1238
|
for (const candidate of candidates) {
|
|
1159
1239
|
const values = new Set();
|
|
1160
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
1183
|
-
return null;
|
|
1265
|
+
return result;
|
|
1184
1266
|
}
|
|
1185
1267
|
function buildJsonSchema(type) {
|
|
1186
1268
|
const defs = {};
|
|
1187
|
-
let
|
|
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" &&
|
|
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,
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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) => {
|