@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/index.mjs
CHANGED
|
@@ -455,28 +455,72 @@ else {
|
|
|
455
455
|
*
|
|
456
456
|
* - **Single PK** (one `@meta.id`) → `static __pk: <scalar type>`
|
|
457
457
|
* - **Compound PK** (multiple `@meta.id`) → `static __pk: { field1: Type1; field2: Type2 }`
|
|
458
|
-
* - **No PK** → no `__pk` emitted
|
|
458
|
+
* - **No PK** → no `__pk` emitted (unless unique indexes exist)
|
|
459
|
+
* - **Unique indexes** (`@db.index.unique`) → appended as union members
|
|
460
|
+
* - **Mongo collection** → always includes `string` (ObjectId) in the union;
|
|
461
|
+
* if no `@meta.id` fields, `__pk` is just `string`
|
|
459
462
|
*/ renderPk(node) {
|
|
463
|
+
const isMongoCollection = !!node.annotations?.some((a) => a.name === "db.mongo.collection");
|
|
460
464
|
let struct;
|
|
461
465
|
if (node.hasExtends) struct = this.doc.resolveInterfaceExtends(node);
|
|
462
466
|
if (!struct) struct = node.getDefinition();
|
|
463
467
|
if (!struct || !isStructure(struct)) return;
|
|
468
|
+
const structNode = struct;
|
|
464
469
|
const pkProps = [];
|
|
465
|
-
|
|
470
|
+
const uniqueByIndex = new Map();
|
|
471
|
+
for (const [name, prop] of structNode.props) {
|
|
466
472
|
if (prop.token("identifier")?.pattern) continue;
|
|
473
|
+
if (isMongoCollection && name === "_id") continue;
|
|
467
474
|
if (prop.countAnnotations("meta.id") > 0) pkProps.push({
|
|
468
475
|
name,
|
|
469
476
|
prop
|
|
470
477
|
});
|
|
478
|
+
if (prop.annotations) {
|
|
479
|
+
for (const ann of prop.annotations) if (ann.name === "db.index.unique") {
|
|
480
|
+
const indexName = ann.args[0]?.text ?? name;
|
|
481
|
+
let group = uniqueByIndex.get(indexName);
|
|
482
|
+
if (!group) {
|
|
483
|
+
group = [];
|
|
484
|
+
uniqueByIndex.set(indexName, group);
|
|
485
|
+
}
|
|
486
|
+
group.push({
|
|
487
|
+
name,
|
|
488
|
+
prop
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
const uniqueProps = [];
|
|
494
|
+
const pkNames = new Set(pkProps.map((p) => p.name));
|
|
495
|
+
for (const fields of uniqueByIndex.values()) if (fields.length === 1 && !pkNames.has(fields[0].name)) uniqueProps.push(fields[0]);
|
|
496
|
+
if (pkProps.length === 0 && uniqueProps.length === 0 && !isMongoCollection) return;
|
|
497
|
+
let mongoIdType;
|
|
498
|
+
if (isMongoCollection) {
|
|
499
|
+
const idProp = structNode.props.get("_id");
|
|
500
|
+
if (idProp) mongoIdType = this.renderTypeDefString(idProp.getDefinition()).trim();
|
|
501
|
+
mongoIdType ?? (mongoIdType = "string");
|
|
502
|
+
}
|
|
503
|
+
const uniqueTypes = [];
|
|
504
|
+
const seenTypes = new Set();
|
|
505
|
+
for (const { prop } of uniqueProps) {
|
|
506
|
+
const rendered = this.renderTypeDefString(prop.getDefinition()).trim();
|
|
507
|
+
if (!seenTypes.has(rendered)) {
|
|
508
|
+
seenTypes.add(rendered);
|
|
509
|
+
uniqueTypes.push(rendered);
|
|
510
|
+
}
|
|
471
511
|
}
|
|
472
|
-
if (pkProps.length === 0) return;
|
|
473
512
|
this.writeln();
|
|
474
|
-
|
|
513
|
+
const uniqueSuffix = uniqueTypes.length > 0 ? ` | ${uniqueTypes.join(" | ")}` : "";
|
|
514
|
+
if (pkProps.length === 0 && !isMongoCollection) this.writeln(`static __pk: ${uniqueTypes.join(" | ")}`);
|
|
515
|
+
else if (pkProps.length === 0) this.writeln(`static __pk: ${mongoIdType}${uniqueSuffix}`);
|
|
516
|
+
else if (pkProps.length === 1) {
|
|
475
517
|
this.write("static __pk: ");
|
|
476
|
-
|
|
477
|
-
renderedDef.
|
|
518
|
+
if (isMongoCollection) this.write(`${mongoIdType} | `);
|
|
519
|
+
const renderedDef = this.renderTypeDefString(pkProps[0].prop.getDefinition()).trim();
|
|
520
|
+
this.writeln(`${renderedDef}${uniqueSuffix}`);
|
|
478
521
|
} else {
|
|
479
522
|
this.write("static __pk: ");
|
|
523
|
+
if (isMongoCollection) this.write(`${mongoIdType} | `);
|
|
480
524
|
this.blockln("{}");
|
|
481
525
|
for (const { name, prop } of pkProps) {
|
|
482
526
|
this.write(wrapProp(name), ": ");
|
|
@@ -484,6 +528,7 @@ else {
|
|
|
484
528
|
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
485
529
|
}
|
|
486
530
|
this.pop();
|
|
531
|
+
this.writeln(uniqueSuffix);
|
|
487
532
|
}
|
|
488
533
|
}
|
|
489
534
|
phantomPropType(def) {
|
|
@@ -971,6 +1016,25 @@ var ValidatorError = class extends Error {
|
|
|
971
1016
|
|
|
972
1017
|
//#endregion
|
|
973
1018
|
//#region packages/typescript/src/annotated-type.ts
|
|
1019
|
+
const COMPLEX_KINDS = new Set([
|
|
1020
|
+
"union",
|
|
1021
|
+
"intersection",
|
|
1022
|
+
"tuple"
|
|
1023
|
+
]);
|
|
1024
|
+
/** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
|
|
1025
|
+
return new Validator(this, opts);
|
|
1026
|
+
}
|
|
1027
|
+
function createAnnotatedTypeNode(type, metadata, opts) {
|
|
1028
|
+
return {
|
|
1029
|
+
__is_atscript_annotated_type: true,
|
|
1030
|
+
type,
|
|
1031
|
+
metadata,
|
|
1032
|
+
validator: validatorMethod,
|
|
1033
|
+
id: opts?.id,
|
|
1034
|
+
optional: opts?.optional,
|
|
1035
|
+
ref: opts?.ref
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
974
1038
|
function isAnnotatedType(type) {
|
|
975
1039
|
return type && type.__is_atscript_annotated_type;
|
|
976
1040
|
}
|
|
@@ -987,38 +1051,24 @@ function defineAnnotatedType(_kind, base) {
|
|
|
987
1051
|
const kind = _kind || "";
|
|
988
1052
|
const type = base?.type || {};
|
|
989
1053
|
type.kind = kind;
|
|
990
|
-
if ([
|
|
991
|
-
"union",
|
|
992
|
-
"intersection",
|
|
993
|
-
"tuple"
|
|
994
|
-
].includes(kind)) type.items = [];
|
|
1054
|
+
if (COMPLEX_KINDS.has(kind)) type.items = [];
|
|
995
1055
|
if (kind === "object") {
|
|
996
1056
|
type.props = new Map();
|
|
997
1057
|
type.propsPatterns = [];
|
|
998
1058
|
}
|
|
999
1059
|
type.tags = new Set();
|
|
1000
1060
|
const metadata = base?.metadata || new Map();
|
|
1001
|
-
|
|
1061
|
+
const payload = {
|
|
1002
1062
|
__is_atscript_annotated_type: true,
|
|
1003
1063
|
metadata,
|
|
1004
1064
|
type,
|
|
1005
|
-
validator
|
|
1006
|
-
return new Validator(this, opts);
|
|
1007
|
-
}
|
|
1008
|
-
});
|
|
1009
|
-
else base = {
|
|
1010
|
-
__is_atscript_annotated_type: true,
|
|
1011
|
-
metadata,
|
|
1012
|
-
type,
|
|
1013
|
-
validator(opts) {
|
|
1014
|
-
return new Validator(this, opts);
|
|
1015
|
-
}
|
|
1065
|
+
validator: validatorMethod
|
|
1016
1066
|
};
|
|
1067
|
+
base = base ? Object.assign(base, payload) : payload;
|
|
1017
1068
|
const handle = {
|
|
1018
1069
|
$type: base,
|
|
1019
1070
|
$def: type,
|
|
1020
1071
|
$metadata: metadata,
|
|
1021
|
-
_existingObject: undefined,
|
|
1022
1072
|
tags(...tags) {
|
|
1023
1073
|
for (const tag of tags) this.$def.tags.add(tag);
|
|
1024
1074
|
return this;
|
|
@@ -1059,26 +1109,56 @@ else base = {
|
|
|
1059
1109
|
return this;
|
|
1060
1110
|
},
|
|
1061
1111
|
refTo(type$1, chain) {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
let
|
|
1066
|
-
|
|
1067
|
-
keys += `["${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];
|
|
1068
1117
|
if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
|
|
1069
|
-
else
|
|
1118
|
+
else {
|
|
1119
|
+
const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
|
|
1120
|
+
throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
1121
|
+
}
|
|
1070
1122
|
}
|
|
1071
|
-
|
|
1072
|
-
else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
1073
|
-
this.$type = {
|
|
1074
|
-
__is_atscript_annotated_type: true,
|
|
1075
|
-
type: newBase.type,
|
|
1076
|
-
metadata,
|
|
1123
|
+
this.$type = createAnnotatedTypeNode(newBase.type, metadata, {
|
|
1077
1124
|
id: newBase.id,
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
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
|
+
});
|
|
1082
1162
|
} else throw new Error(`${type$1} is not annotated type`);
|
|
1083
1163
|
return this;
|
|
1084
1164
|
},
|
|
@@ -1129,10 +1209,10 @@ function forAnnotatedType(def, handlers) {
|
|
|
1129
1209
|
const firstObj = items[0].type;
|
|
1130
1210
|
const candidates = [];
|
|
1131
1211
|
for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
|
|
1132
|
-
|
|
1212
|
+
let result = null;
|
|
1133
1213
|
for (const candidate of candidates) {
|
|
1134
1214
|
const values = new Set();
|
|
1135
|
-
const
|
|
1215
|
+
const indexMapping = {};
|
|
1136
1216
|
let valid = true;
|
|
1137
1217
|
for (let i = 0; i < items.length; i++) {
|
|
1138
1218
|
const obj = items[i].type;
|
|
@@ -1147,19 +1227,21 @@ function forAnnotatedType(def, handlers) {
|
|
|
1147
1227
|
break;
|
|
1148
1228
|
}
|
|
1149
1229
|
values.add(val);
|
|
1150
|
-
|
|
1230
|
+
indexMapping[String(val)] = i;
|
|
1231
|
+
}
|
|
1232
|
+
if (valid) {
|
|
1233
|
+
if (result) return null;
|
|
1234
|
+
result = {
|
|
1235
|
+
propertyName: candidate,
|
|
1236
|
+
indexMapping
|
|
1237
|
+
};
|
|
1151
1238
|
}
|
|
1152
|
-
if (valid) validCandidates.push({
|
|
1153
|
-
propertyName: candidate,
|
|
1154
|
-
mapping
|
|
1155
|
-
});
|
|
1156
1239
|
}
|
|
1157
|
-
|
|
1158
|
-
return null;
|
|
1240
|
+
return result;
|
|
1159
1241
|
}
|
|
1160
1242
|
function buildJsonSchema(type) {
|
|
1161
1243
|
const defs = {};
|
|
1162
|
-
let
|
|
1244
|
+
let hasDefs = false;
|
|
1163
1245
|
const buildObject = (d) => {
|
|
1164
1246
|
const properties = {};
|
|
1165
1247
|
const required = [];
|
|
@@ -1176,15 +1258,15 @@ function buildJsonSchema(type) {
|
|
|
1176
1258
|
return schema$1;
|
|
1177
1259
|
};
|
|
1178
1260
|
const build = (def) => {
|
|
1179
|
-
if (def.id && def.type.kind === "object" &&
|
|
1261
|
+
if (def.id && def.type.kind === "object" && def !== type) {
|
|
1180
1262
|
const name = def.id;
|
|
1181
1263
|
if (!defs[name]) {
|
|
1264
|
+
hasDefs = true;
|
|
1182
1265
|
defs[name] = {};
|
|
1183
1266
|
defs[name] = buildObject(def);
|
|
1184
1267
|
}
|
|
1185
1268
|
return { $ref: `#/$defs/${name}` };
|
|
1186
1269
|
}
|
|
1187
|
-
isRoot = false;
|
|
1188
1270
|
const meta = def.metadata;
|
|
1189
1271
|
return forAnnotatedType(def, {
|
|
1190
1272
|
phantom() {
|
|
@@ -1209,11 +1291,9 @@ function buildJsonSchema(type) {
|
|
|
1209
1291
|
if (disc) {
|
|
1210
1292
|
const oneOf = d.type.items.map(build);
|
|
1211
1293
|
const mapping = {};
|
|
1212
|
-
for (const [val,
|
|
1213
|
-
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
1294
|
+
for (const [val, idx] of Object.entries(disc.indexMapping)) {
|
|
1214
1295
|
const item = d.type.items[idx];
|
|
1215
|
-
|
|
1216
|
-
else mapping[val] = origPath;
|
|
1296
|
+
mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
|
|
1217
1297
|
}
|
|
1218
1298
|
return {
|
|
1219
1299
|
oneOf,
|
|
@@ -1263,7 +1343,7 @@ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ patte
|
|
|
1263
1343
|
});
|
|
1264
1344
|
};
|
|
1265
1345
|
const schema = build(type);
|
|
1266
|
-
if (
|
|
1346
|
+
if (hasDefs) return {
|
|
1267
1347
|
...schema,
|
|
1268
1348
|
$defs: defs
|
|
1269
1349
|
};
|
|
@@ -1564,8 +1644,8 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1564
1644
|
const ref = node;
|
|
1565
1645
|
const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
|
|
1566
1646
|
if (isPrimitive(decl)) {
|
|
1567
|
-
const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
|
|
1568
|
-
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") {
|
|
1569
1649
|
this.annotateType(decl, name);
|
|
1570
1650
|
return this;
|
|
1571
1651
|
}
|
|
@@ -1579,9 +1659,11 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1579
1659
|
}
|
|
1580
1660
|
}
|
|
1581
1661
|
const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
|
|
1582
|
-
|
|
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})`);
|
|
1583
1666
|
if (!ref.hasChain) {
|
|
1584
|
-
const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
|
|
1585
1667
|
if (ownerDecl?.node) {
|
|
1586
1668
|
const typeAnnotations = ownerDecl.doc.evalAnnotationsForNode(ownerDecl.node);
|
|
1587
1669
|
typeAnnotations?.forEach((an) => {
|