@hyphaene/hexa-ts-kit 1.12.1 → 1.13.0
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/{chunk-EB6S5X3P.js → chunk-73G2DONK.js} +339 -275
- package/dist/cli.js +31 -46
- package/dist/mcp-server.js +1 -1
- package/package.json +5 -8
|
@@ -1043,7 +1043,7 @@ function getVaultConfig(config) {
|
|
|
1043
1043
|
|
|
1044
1044
|
// src/lib/contracts/permissions-extractor.ts
|
|
1045
1045
|
import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
|
|
1046
|
-
import {
|
|
1046
|
+
import { Project, SyntaxKind } from "ts-morph";
|
|
1047
1047
|
function extractValues(sourcePath, config) {
|
|
1048
1048
|
if (!existsSync3(sourcePath)) {
|
|
1049
1049
|
return {
|
|
@@ -1062,12 +1062,13 @@ function extractValues(sourcePath, config) {
|
|
|
1062
1062
|
sourcePath
|
|
1063
1063
|
};
|
|
1064
1064
|
}
|
|
1065
|
-
|
|
1065
|
+
const project = new Project({
|
|
1066
|
+
useInMemoryFileSystem: true,
|
|
1067
|
+
skipAddingFilesFromTsConfig: true
|
|
1068
|
+
});
|
|
1069
|
+
let sourceFile;
|
|
1066
1070
|
try {
|
|
1067
|
-
|
|
1068
|
-
loc: true,
|
|
1069
|
-
range: true
|
|
1070
|
-
});
|
|
1071
|
+
sourceFile = project.createSourceFile("temp.ts", content);
|
|
1071
1072
|
} catch (err) {
|
|
1072
1073
|
return {
|
|
1073
1074
|
success: false,
|
|
@@ -1078,9 +1079,9 @@ function extractValues(sourcePath, config) {
|
|
|
1078
1079
|
const exportName = config.export;
|
|
1079
1080
|
const extractMode = config.extract;
|
|
1080
1081
|
if (extractMode === "const-values") {
|
|
1081
|
-
return extractConstValues(
|
|
1082
|
+
return extractConstValues(sourceFile, exportName, sourcePath);
|
|
1082
1083
|
} else if (extractMode === "zod-keys") {
|
|
1083
|
-
return extractZodKeys(
|
|
1084
|
+
return extractZodKeys(sourceFile, exportName, sourcePath);
|
|
1084
1085
|
}
|
|
1085
1086
|
return {
|
|
1086
1087
|
success: false,
|
|
@@ -1088,33 +1089,34 @@ function extractValues(sourcePath, config) {
|
|
|
1088
1089
|
sourcePath
|
|
1089
1090
|
};
|
|
1090
1091
|
}
|
|
1091
|
-
function extractConstValues(
|
|
1092
|
+
function extractConstValues(sourceFile, exportName, sourcePath) {
|
|
1092
1093
|
const values = [];
|
|
1093
1094
|
const keyToValue = {};
|
|
1094
|
-
for (const
|
|
1095
|
-
if (
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1095
|
+
for (const varStmt of sourceFile.getVariableStatements()) {
|
|
1096
|
+
if (!varStmt.isExported()) continue;
|
|
1097
|
+
for (const decl of varStmt.getDeclarations()) {
|
|
1098
|
+
if (decl.getName() !== exportName) continue;
|
|
1099
|
+
const init = decl.getInitializer();
|
|
1100
|
+
if (!init) continue;
|
|
1101
|
+
let objLiteral = init.asKind(SyntaxKind.ObjectLiteralExpression);
|
|
1102
|
+
if (!objLiteral) {
|
|
1103
|
+
const asExpr = init.asKind(SyntaxKind.AsExpression);
|
|
1104
|
+
if (asExpr) {
|
|
1105
|
+
objLiteral = asExpr.getExpression().asKind(SyntaxKind.ObjectLiteralExpression);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (!objLiteral) continue;
|
|
1109
|
+
for (const prop of objLiteral.getProperties()) {
|
|
1110
|
+
const propAssign = prop.asKind(SyntaxKind.PropertyAssignment);
|
|
1111
|
+
if (!propAssign) continue;
|
|
1112
|
+
const val = propAssign.getInitializer();
|
|
1113
|
+
const strLiteral = val?.asKind(SyntaxKind.StringLiteral);
|
|
1114
|
+
if (!strLiteral) continue;
|
|
1115
|
+
const value = strLiteral.getLiteralValue();
|
|
1116
|
+
values.push(value);
|
|
1117
|
+
const keyNode = propAssign.getNameNode();
|
|
1118
|
+
if (keyNode.getKind() === SyntaxKind.Identifier) {
|
|
1119
|
+
keyToValue[keyNode.getText()] = value;
|
|
1118
1120
|
}
|
|
1119
1121
|
}
|
|
1120
1122
|
}
|
|
@@ -1133,24 +1135,26 @@ function extractConstValues(ast, exportName, sourcePath) {
|
|
|
1133
1135
|
sourcePath
|
|
1134
1136
|
};
|
|
1135
1137
|
}
|
|
1136
|
-
function extractZodKeys(
|
|
1138
|
+
function extractZodKeys(sourceFile, exportName, sourcePath) {
|
|
1137
1139
|
const values = [];
|
|
1138
|
-
for (const
|
|
1139
|
-
if (
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1140
|
+
for (const varStmt of sourceFile.getVariableStatements()) {
|
|
1141
|
+
if (!varStmt.isExported()) continue;
|
|
1142
|
+
for (const decl of varStmt.getDeclarations()) {
|
|
1143
|
+
if (decl.getName() !== exportName) continue;
|
|
1144
|
+
const init = decl.getInitializer();
|
|
1145
|
+
if (!init) continue;
|
|
1146
|
+
const callExpr = init.asKind(SyntaxKind.CallExpression);
|
|
1147
|
+
if (!callExpr) continue;
|
|
1148
|
+
const callee = callExpr.getExpression();
|
|
1149
|
+
if (!callee.getText().endsWith(".object")) continue;
|
|
1150
|
+
const firstArg = callExpr.getArguments()[0];
|
|
1151
|
+
if (!firstArg) continue;
|
|
1152
|
+
const objLiteral = firstArg.asKind(SyntaxKind.ObjectLiteralExpression);
|
|
1153
|
+
if (!objLiteral) continue;
|
|
1154
|
+
for (const prop of objLiteral.getProperties()) {
|
|
1155
|
+
const propAssign = prop.asKind(SyntaxKind.PropertyAssignment);
|
|
1156
|
+
if (!propAssign) continue;
|
|
1157
|
+
values.push(propAssign.getName());
|
|
1154
1158
|
}
|
|
1155
1159
|
}
|
|
1156
1160
|
}
|
|
@@ -1171,7 +1175,7 @@ function extractZodKeys(ast, exportName, sourcePath) {
|
|
|
1171
1175
|
// src/lib/contracts/contract-parser.ts
|
|
1172
1176
|
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
1173
1177
|
import fg7 from "fast-glob";
|
|
1174
|
-
import {
|
|
1178
|
+
import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
1175
1179
|
async function findContractFiles(repoPath) {
|
|
1176
1180
|
const files = await fg7("**/*.contract.ts", {
|
|
1177
1181
|
cwd: repoPath,
|
|
@@ -1198,12 +1202,13 @@ function parseContractFile(filePath) {
|
|
|
1198
1202
|
filePath
|
|
1199
1203
|
};
|
|
1200
1204
|
}
|
|
1201
|
-
|
|
1205
|
+
const project = new Project2({
|
|
1206
|
+
useInMemoryFileSystem: true,
|
|
1207
|
+
skipAddingFilesFromTsConfig: true
|
|
1208
|
+
});
|
|
1209
|
+
let sourceFile;
|
|
1202
1210
|
try {
|
|
1203
|
-
|
|
1204
|
-
loc: true,
|
|
1205
|
-
range: true
|
|
1206
|
-
});
|
|
1211
|
+
sourceFile = project.createSourceFile("contract.ts", content);
|
|
1207
1212
|
} catch (err) {
|
|
1208
1213
|
return {
|
|
1209
1214
|
success: false,
|
|
@@ -1213,20 +1218,15 @@ function parseContractFile(filePath) {
|
|
|
1213
1218
|
}
|
|
1214
1219
|
const endpoints = [];
|
|
1215
1220
|
let contractName = "UnknownContract";
|
|
1216
|
-
for (const
|
|
1217
|
-
if (
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
content
|
|
1226
|
-
);
|
|
1227
|
-
endpoints.push(...parsedEndpoints);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1221
|
+
for (const varStmt of sourceFile.getVariableStatements()) {
|
|
1222
|
+
if (!varStmt.isExported()) continue;
|
|
1223
|
+
for (const decl of varStmt.getDeclarations()) {
|
|
1224
|
+
contractName = decl.getName();
|
|
1225
|
+
const init = decl.getInitializer();
|
|
1226
|
+
const routerObj = findRouterObjectArg(init);
|
|
1227
|
+
if (routerObj) {
|
|
1228
|
+
const parsedEndpoints = extractEndpointsFromRouter(routerObj);
|
|
1229
|
+
endpoints.push(...parsedEndpoints);
|
|
1230
1230
|
}
|
|
1231
1231
|
}
|
|
1232
1232
|
}
|
|
@@ -1265,31 +1265,33 @@ async function parseContracts(repoPath) {
|
|
|
1265
1265
|
contracts
|
|
1266
1266
|
};
|
|
1267
1267
|
}
|
|
1268
|
-
function
|
|
1268
|
+
function findRouterObjectArg(node) {
|
|
1269
1269
|
if (!node) return null;
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1270
|
+
const callExpr = node.asKind(SyntaxKind2.CallExpression);
|
|
1271
|
+
if (!callExpr) return null;
|
|
1272
|
+
const callee = callExpr.getExpression();
|
|
1273
|
+
const propAccess = callee.asKind(SyntaxKind2.PropertyAccessExpression);
|
|
1274
|
+
if (!propAccess || propAccess.getName() !== "router") return null;
|
|
1275
|
+
const firstArg = callExpr.getArguments()[0];
|
|
1276
|
+
if (!firstArg) return null;
|
|
1277
|
+
return firstArg.asKind(SyntaxKind2.ObjectLiteralExpression) ?? null;
|
|
1278
|
+
}
|
|
1279
|
+
function extractEndpointsFromRouter(routerObj) {
|
|
1276
1280
|
const endpoints = [];
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
if (prop.value.type !== "ObjectExpression") continue;
|
|
1281
|
+
for (const prop of routerObj.getProperties()) {
|
|
1282
|
+
const propAssign = prop.asKind(SyntaxKind2.PropertyAssignment);
|
|
1283
|
+
if (!propAssign) continue;
|
|
1284
|
+
const endpointName = propAssign.getName();
|
|
1285
|
+
const endpointLine = propAssign.getStartLineNumber();
|
|
1286
|
+
const endpointValue = propAssign.getInitializer();
|
|
1287
|
+
const endpointObj = endpointValue?.asKind(
|
|
1288
|
+
SyntaxKind2.ObjectLiteralExpression
|
|
1289
|
+
);
|
|
1290
|
+
if (!endpointObj) continue;
|
|
1288
1291
|
const endpoint = extractEndpointDetails(
|
|
1289
1292
|
endpointName,
|
|
1290
|
-
|
|
1291
|
-
endpointLine
|
|
1292
|
-
sourceContent
|
|
1293
|
+
endpointObj,
|
|
1294
|
+
endpointLine
|
|
1293
1295
|
);
|
|
1294
1296
|
if (endpoint) {
|
|
1295
1297
|
endpoints.push(endpoint);
|
|
@@ -1297,28 +1299,34 @@ function extractEndpointsFromRouter(routerCall, sourceContent) {
|
|
|
1297
1299
|
}
|
|
1298
1300
|
return endpoints;
|
|
1299
1301
|
}
|
|
1300
|
-
function extractEndpointDetails(name,
|
|
1302
|
+
function extractEndpointDetails(name, endpointObj, line) {
|
|
1301
1303
|
let method = "";
|
|
1302
1304
|
let path = "";
|
|
1303
1305
|
let metadata;
|
|
1304
1306
|
let strictStatusCodes;
|
|
1305
|
-
for (const prop of
|
|
1306
|
-
|
|
1307
|
-
if (
|
|
1308
|
-
const propName =
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
}
|
|
1315
|
-
if (propName === "
|
|
1316
|
-
|
|
1317
|
-
|
|
1307
|
+
for (const prop of endpointObj.getProperties()) {
|
|
1308
|
+
const propAssign = prop.asKind(SyntaxKind2.PropertyAssignment);
|
|
1309
|
+
if (!propAssign) continue;
|
|
1310
|
+
const propName = propAssign.getName();
|
|
1311
|
+
const propVal = propAssign.getInitializer();
|
|
1312
|
+
if (!propVal) continue;
|
|
1313
|
+
if (propName === "method") {
|
|
1314
|
+
const strLit = propVal.asKind(SyntaxKind2.StringLiteral);
|
|
1315
|
+
if (strLit) method = strLit.getLiteralValue();
|
|
1316
|
+
}
|
|
1317
|
+
if (propName === "path") {
|
|
1318
|
+
const strLit = propVal.asKind(SyntaxKind2.StringLiteral);
|
|
1319
|
+
if (strLit) path = strLit.getLiteralValue();
|
|
1320
|
+
}
|
|
1321
|
+
if (propName === "strictStatusCodes") {
|
|
1322
|
+
if (propVal.getKind() === SyntaxKind2.TrueKeyword) {
|
|
1323
|
+
strictStatusCodes = true;
|
|
1324
|
+
} else if (propVal.getKind() === SyntaxKind2.FalseKeyword) {
|
|
1325
|
+
strictStatusCodes = false;
|
|
1318
1326
|
}
|
|
1319
1327
|
}
|
|
1320
|
-
if (propName === "
|
|
1321
|
-
|
|
1328
|
+
if (propName === "metadata") {
|
|
1329
|
+
metadata = extractMetadata(propVal);
|
|
1322
1330
|
}
|
|
1323
1331
|
}
|
|
1324
1332
|
return {
|
|
@@ -1330,82 +1338,105 @@ function extractEndpointDetails(name, objectExpr, line, sourceContent) {
|
|
|
1330
1338
|
line
|
|
1331
1339
|
};
|
|
1332
1340
|
}
|
|
1333
|
-
function extractMetadata(node
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
+
function extractMetadata(node) {
|
|
1342
|
+
const objLiteral = node.asKind(SyntaxKind2.ObjectLiteralExpression);
|
|
1343
|
+
if (objLiteral) {
|
|
1344
|
+
return parseMetadataObject(objLiteral);
|
|
1345
|
+
}
|
|
1346
|
+
const callExpr = node.asKind(SyntaxKind2.CallExpression);
|
|
1347
|
+
if (callExpr) {
|
|
1348
|
+
const callee = callExpr.getExpression();
|
|
1349
|
+
if (callee.getKind() === SyntaxKind2.Identifier && callee.getText() === "contractMetadata") {
|
|
1350
|
+
const firstArg = callExpr.getArguments()[0];
|
|
1351
|
+
if (firstArg) {
|
|
1352
|
+
const argObj = firstArg.asKind(SyntaxKind2.ObjectLiteralExpression);
|
|
1353
|
+
if (argObj) {
|
|
1354
|
+
return parseMetadataObject(argObj);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1341
1357
|
}
|
|
1342
1358
|
}
|
|
1343
1359
|
return void 0;
|
|
1344
1360
|
}
|
|
1345
|
-
function parseMetadataObject(
|
|
1361
|
+
function parseMetadataObject(objLiteral) {
|
|
1346
1362
|
const metadata = {};
|
|
1347
|
-
for (const prop of
|
|
1348
|
-
|
|
1349
|
-
if (
|
|
1350
|
-
const propName =
|
|
1351
|
-
|
|
1352
|
-
|
|
1363
|
+
for (const prop of objLiteral.getProperties()) {
|
|
1364
|
+
const propAssign = prop.asKind(SyntaxKind2.PropertyAssignment);
|
|
1365
|
+
if (!propAssign) continue;
|
|
1366
|
+
const propName = propAssign.getName();
|
|
1367
|
+
const propVal = propAssign.getInitializer();
|
|
1368
|
+
if (!propVal) continue;
|
|
1369
|
+
if (propName === "openApiTags") {
|
|
1370
|
+
const arr = propVal.asKind(SyntaxKind2.ArrayLiteralExpression);
|
|
1371
|
+
if (arr) {
|
|
1372
|
+
metadata.openApiTags = extractStringArray(arr);
|
|
1373
|
+
}
|
|
1353
1374
|
}
|
|
1354
1375
|
if (propName === "requiredPermission") {
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
metadata.requiredPermission = permValue;
|
|
1359
|
-
}
|
|
1376
|
+
const permValue = extractPermissionValue(propVal);
|
|
1377
|
+
if (permValue) {
|
|
1378
|
+
metadata.requiredPermission = permValue;
|
|
1360
1379
|
}
|
|
1361
1380
|
}
|
|
1362
|
-
if (propName === "requiresAuth"
|
|
1363
|
-
metadata.requiresAuth =
|
|
1381
|
+
if (propName === "requiresAuth") {
|
|
1382
|
+
metadata.requiresAuth = propVal.getKind() === SyntaxKind2.TrueKeyword;
|
|
1364
1383
|
}
|
|
1365
|
-
if (propName === "bff"
|
|
1366
|
-
|
|
1384
|
+
if (propName === "bff") {
|
|
1385
|
+
const bffObj = propVal.asKind(SyntaxKind2.ObjectLiteralExpression);
|
|
1386
|
+
if (bffObj) {
|
|
1387
|
+
metadata.bff = extractBffConfig(bffObj);
|
|
1388
|
+
}
|
|
1367
1389
|
}
|
|
1368
|
-
if (propName === "openapi"
|
|
1369
|
-
|
|
1390
|
+
if (propName === "openapi") {
|
|
1391
|
+
const openapiObj = propVal.asKind(SyntaxKind2.ObjectLiteralExpression);
|
|
1392
|
+
if (openapiObj) {
|
|
1393
|
+
metadata.openapi = extractLegacyOpenApi(openapiObj);
|
|
1394
|
+
}
|
|
1370
1395
|
}
|
|
1371
1396
|
}
|
|
1372
1397
|
return metadata;
|
|
1373
1398
|
}
|
|
1374
1399
|
function extractStringArray(arrayExpr) {
|
|
1375
1400
|
const values = [];
|
|
1376
|
-
for (const element of arrayExpr.
|
|
1377
|
-
|
|
1378
|
-
|
|
1401
|
+
for (const element of arrayExpr.getElements()) {
|
|
1402
|
+
const strLit = element.asKind(SyntaxKind2.StringLiteral);
|
|
1403
|
+
if (strLit) {
|
|
1404
|
+
values.push(strLit.getLiteralValue());
|
|
1379
1405
|
}
|
|
1380
1406
|
}
|
|
1381
1407
|
return values;
|
|
1382
1408
|
}
|
|
1383
|
-
function extractPermissionValue(node
|
|
1384
|
-
|
|
1385
|
-
|
|
1409
|
+
function extractPermissionValue(node) {
|
|
1410
|
+
const strLit = node.asKind(SyntaxKind2.StringLiteral);
|
|
1411
|
+
if (strLit) {
|
|
1412
|
+
return strLit.getLiteralValue();
|
|
1386
1413
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
return sourceContent.slice(start, end);
|
|
1391
|
-
}
|
|
1414
|
+
const propAccess = node.asKind(SyntaxKind2.PropertyAccessExpression);
|
|
1415
|
+
if (propAccess) {
|
|
1416
|
+
return propAccess.getText();
|
|
1392
1417
|
}
|
|
1393
1418
|
return void 0;
|
|
1394
1419
|
}
|
|
1395
|
-
function extractBffConfig(
|
|
1420
|
+
function extractBffConfig(objLiteral) {
|
|
1396
1421
|
const bff = {};
|
|
1397
|
-
for (const prop of
|
|
1398
|
-
|
|
1399
|
-
if (
|
|
1400
|
-
if (
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1422
|
+
for (const prop of objLiteral.getProperties()) {
|
|
1423
|
+
const propAssign = prop.asKind(SyntaxKind2.PropertyAssignment);
|
|
1424
|
+
if (!propAssign) continue;
|
|
1425
|
+
if (propAssign.getName() === "apiServices") {
|
|
1426
|
+
const arr = propAssign.getInitializer()?.asKind(SyntaxKind2.ArrayLiteralExpression);
|
|
1427
|
+
if (arr) {
|
|
1428
|
+
bff.apiServices = [];
|
|
1429
|
+
for (const element of arr.getElements()) {
|
|
1430
|
+
const strLit = element.asKind(SyntaxKind2.StringLiteral);
|
|
1431
|
+
if (strLit) {
|
|
1432
|
+
bff.apiServices.push(strLit.getLiteralValue());
|
|
1433
|
+
continue;
|
|
1434
|
+
}
|
|
1435
|
+
const propAccess = element.asKind(
|
|
1436
|
+
SyntaxKind2.PropertyAccessExpression
|
|
1437
|
+
);
|
|
1438
|
+
if (propAccess) {
|
|
1439
|
+
bff.apiServices.push(propAccess.getName());
|
|
1409
1440
|
}
|
|
1410
1441
|
}
|
|
1411
1442
|
}
|
|
@@ -1413,13 +1444,16 @@ function extractBffConfig(objectExpr) {
|
|
|
1413
1444
|
}
|
|
1414
1445
|
return bff;
|
|
1415
1446
|
}
|
|
1416
|
-
function extractLegacyOpenApi(
|
|
1447
|
+
function extractLegacyOpenApi(objLiteral) {
|
|
1417
1448
|
const openapi = {};
|
|
1418
|
-
for (const prop of
|
|
1419
|
-
|
|
1420
|
-
if (
|
|
1421
|
-
if (
|
|
1422
|
-
|
|
1449
|
+
for (const prop of objLiteral.getProperties()) {
|
|
1450
|
+
const propAssign = prop.asKind(SyntaxKind2.PropertyAssignment);
|
|
1451
|
+
if (!propAssign) continue;
|
|
1452
|
+
if (propAssign.getName() === "tags") {
|
|
1453
|
+
const arr = propAssign.getInitializer()?.asKind(SyntaxKind2.ArrayLiteralExpression);
|
|
1454
|
+
if (arr) {
|
|
1455
|
+
openapi.tags = extractStringArray(arr);
|
|
1456
|
+
}
|
|
1423
1457
|
}
|
|
1424
1458
|
}
|
|
1425
1459
|
return openapi;
|
|
@@ -2392,7 +2426,7 @@ function checkSchemaImport(contractFile, _contractDir, imp) {
|
|
|
2392
2426
|
import fg9 from "fast-glob";
|
|
2393
2427
|
import { readFileSync as readFileSync4 } from "fs";
|
|
2394
2428
|
import { basename as basename5 } from "path";
|
|
2395
|
-
import {
|
|
2429
|
+
import { Node, Project as Project3, SyntaxKind as SyntaxKind3 } from "ts-morph";
|
|
2396
2430
|
var ALLOWED_SUFFIXES = [
|
|
2397
2431
|
".enum-bundle.ts",
|
|
2398
2432
|
".schema.ts",
|
|
@@ -2409,15 +2443,16 @@ var ALLOWED_SUFFIXES = [
|
|
|
2409
2443
|
".rules.ts"
|
|
2410
2444
|
];
|
|
2411
2445
|
var ALLOWED_SPECIAL_FILES = ["index.ts", "openapi.ts"];
|
|
2412
|
-
function
|
|
2446
|
+
function createProject() {
|
|
2447
|
+
return new Project3({
|
|
2448
|
+
useInMemoryFileSystem: true,
|
|
2449
|
+
skipAddingFilesFromTsConfig: true
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
function parseFile(filePath, project) {
|
|
2413
2453
|
try {
|
|
2414
2454
|
const content = readFileSync4(filePath, "utf-8");
|
|
2415
|
-
return
|
|
2416
|
-
loc: true,
|
|
2417
|
-
range: true,
|
|
2418
|
-
comment: true,
|
|
2419
|
-
jsx: false
|
|
2420
|
-
});
|
|
2455
|
+
return project.createSourceFile(filePath, content, { overwrite: true });
|
|
2421
2456
|
} catch {
|
|
2422
2457
|
return null;
|
|
2423
2458
|
}
|
|
@@ -2425,48 +2460,58 @@ function parseFile(filePath) {
|
|
|
2425
2460
|
function readFileContent(filePath) {
|
|
2426
2461
|
return readFileSync4(filePath, "utf-8");
|
|
2427
2462
|
}
|
|
2428
|
-
function getExportedNames(
|
|
2463
|
+
function getExportedNames(sourceFile) {
|
|
2429
2464
|
const names = [];
|
|
2430
|
-
for (const
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2465
|
+
for (const statement of sourceFile.getStatements()) {
|
|
2466
|
+
const kind = statement.getKind();
|
|
2467
|
+
if (kind === SyntaxKind3.ExportDeclaration) {
|
|
2468
|
+
const exportDecl = statement.asKindOrThrow(SyntaxKind3.ExportDeclaration);
|
|
2469
|
+
for (const namedExport of exportDecl.getNamedExports()) {
|
|
2470
|
+
names.push(
|
|
2471
|
+
namedExport.getAliasNode()?.getText() ?? namedExport.getName()
|
|
2472
|
+
);
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
if (Node.isModifierable(statement) && statement.hasModifier(SyntaxKind3.ExportKeyword)) {
|
|
2476
|
+
if (kind === SyntaxKind3.VariableStatement) {
|
|
2477
|
+
const varStatement = statement.asKindOrThrow(
|
|
2478
|
+
SyntaxKind3.VariableStatement
|
|
2479
|
+
);
|
|
2480
|
+
for (const decl of varStatement.getDeclarations()) {
|
|
2481
|
+
const nameNode = decl.getNameNode();
|
|
2482
|
+
if (nameNode.getKind() === SyntaxKind3.Identifier) {
|
|
2483
|
+
names.push(nameNode.getText());
|
|
2484
|
+
} else if (nameNode.getKind() === SyntaxKind3.ObjectBindingPattern) {
|
|
2485
|
+
const objectPattern = nameNode.asKindOrThrow(
|
|
2486
|
+
SyntaxKind3.ObjectBindingPattern
|
|
2487
|
+
);
|
|
2488
|
+
for (const element of objectPattern.getElements()) {
|
|
2489
|
+
const propertyName = element.getPropertyNameNode();
|
|
2490
|
+
const elementName = element.getNameNode();
|
|
2491
|
+
names.push(elementName.getText());
|
|
2443
2492
|
}
|
|
2444
2493
|
}
|
|
2445
2494
|
}
|
|
2446
|
-
if (node.declaration.type === "TSTypeAliasDeclaration") {
|
|
2447
|
-
names.push(node.declaration.id.name);
|
|
2448
|
-
}
|
|
2449
|
-
if (node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
|
|
2450
|
-
names.push(node.declaration.id.name);
|
|
2451
|
-
}
|
|
2452
2495
|
}
|
|
2453
|
-
if (
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2496
|
+
if (kind === SyntaxKind3.TypeAliasDeclaration) {
|
|
2497
|
+
const typeAlias = statement.asKindOrThrow(
|
|
2498
|
+
SyntaxKind3.TypeAliasDeclaration
|
|
2499
|
+
);
|
|
2500
|
+
names.push(typeAlias.getName());
|
|
2501
|
+
}
|
|
2502
|
+
if (kind === SyntaxKind3.FunctionDeclaration) {
|
|
2503
|
+
const funcDecl = statement.asKindOrThrow(
|
|
2504
|
+
SyntaxKind3.FunctionDeclaration
|
|
2505
|
+
);
|
|
2506
|
+
const funcName = funcDecl.getName();
|
|
2507
|
+
if (funcName) names.push(funcName);
|
|
2459
2508
|
}
|
|
2460
2509
|
}
|
|
2461
2510
|
}
|
|
2462
2511
|
return names;
|
|
2463
2512
|
}
|
|
2464
|
-
function hasDefaultExport(
|
|
2465
|
-
return
|
|
2466
|
-
(node) => node.type === "ExportDefaultDeclaration" || node.type === "ExportNamedDeclaration" && node.specifiers?.some(
|
|
2467
|
-
(s) => s.exported.type === "Identifier" && s.exported.name === "default"
|
|
2468
|
-
)
|
|
2469
|
-
);
|
|
2513
|
+
function hasDefaultExport(sourceFile) {
|
|
2514
|
+
return sourceFile.getExportedDeclarations().has("default");
|
|
2470
2515
|
}
|
|
2471
2516
|
function hasOpenApiDocumentation(content) {
|
|
2472
2517
|
return /\.describe\s*\(/.test(content) || /\.openapi\s*\(/.test(content);
|
|
@@ -2482,47 +2527,66 @@ function hasZodDataSchemas(content) {
|
|
|
2482
2527
|
content
|
|
2483
2528
|
);
|
|
2484
2529
|
}
|
|
2485
|
-
function getExportsWithoutDeprecatedJsdoc(
|
|
2530
|
+
function getExportsWithoutDeprecatedJsdoc(sourceFile) {
|
|
2486
2531
|
const missingDeprecated = [];
|
|
2487
|
-
for (const
|
|
2488
|
-
|
|
2532
|
+
for (const statement of sourceFile.getStatements()) {
|
|
2533
|
+
const kind = statement.getKind();
|
|
2534
|
+
const isExported = Node.isModifierable(statement) && statement.hasModifier(SyntaxKind3.ExportKeyword);
|
|
2535
|
+
if (kind !== SyntaxKind3.ExportDeclaration && kind !== SyntaxKind3.ExportAssignment && !isExported) {
|
|
2489
2536
|
continue;
|
|
2490
2537
|
}
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
if (node.declaration.type === "VariableDeclaration") {
|
|
2495
|
-
const decl = node.declaration.declarations[0];
|
|
2496
|
-
if (decl?.id.type === "Identifier") {
|
|
2497
|
-
exportName = decl.id.name;
|
|
2498
|
-
}
|
|
2499
|
-
} else if (node.declaration.type === "TSTypeAliasDeclaration" || node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
|
|
2500
|
-
exportName = node.declaration.type === "TSTypeAliasDeclaration" ? node.declaration.id.name : node.declaration.id.name;
|
|
2501
|
-
}
|
|
2538
|
+
if (kind === SyntaxKind3.ExportAssignment) {
|
|
2539
|
+
if (!hasDeprecatedComment(statement)) {
|
|
2540
|
+
missingDeprecated.push("default");
|
|
2502
2541
|
}
|
|
2503
|
-
|
|
2542
|
+
continue;
|
|
2543
|
+
}
|
|
2544
|
+
if (kind === SyntaxKind3.ExportDeclaration) {
|
|
2545
|
+
const exportDecl = statement.asKindOrThrow(SyntaxKind3.ExportDeclaration);
|
|
2546
|
+
if (exportDecl.getModuleSpecifier() !== void 0) continue;
|
|
2504
2547
|
}
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2548
|
+
let exportName = "unknown";
|
|
2549
|
+
if (kind === SyntaxKind3.VariableStatement) {
|
|
2550
|
+
const varStatement = statement.asKindOrThrow(
|
|
2551
|
+
SyntaxKind3.VariableStatement
|
|
2552
|
+
);
|
|
2553
|
+
const declarations = varStatement.getDeclarations();
|
|
2554
|
+
const firstDecl = declarations[0];
|
|
2555
|
+
if (firstDecl) {
|
|
2556
|
+
exportName = firstDecl.getName();
|
|
2557
|
+
}
|
|
2558
|
+
} else if (kind === SyntaxKind3.TypeAliasDeclaration) {
|
|
2559
|
+
const typeAlias = statement.asKindOrThrow(
|
|
2560
|
+
SyntaxKind3.TypeAliasDeclaration
|
|
2561
|
+
);
|
|
2562
|
+
exportName = typeAlias.getName();
|
|
2563
|
+
} else if (kind === SyntaxKind3.FunctionDeclaration) {
|
|
2564
|
+
const funcDecl = statement.asKindOrThrow(SyntaxKind3.FunctionDeclaration);
|
|
2565
|
+
exportName = funcDecl.getName() ?? "anonymous";
|
|
2566
|
+
}
|
|
2567
|
+
if (!hasDeprecatedComment(statement)) {
|
|
2508
2568
|
missingDeprecated.push(exportName);
|
|
2509
2569
|
}
|
|
2510
2570
|
}
|
|
2511
2571
|
return missingDeprecated;
|
|
2512
2572
|
}
|
|
2513
|
-
function
|
|
2573
|
+
function hasDeprecatedComment(node) {
|
|
2574
|
+
return node.getLeadingCommentRanges().some((comment) => comment.getText().includes("@deprecated"));
|
|
2575
|
+
}
|
|
2576
|
+
function getNonReexportStatements(sourceFile) {
|
|
2514
2577
|
const violations = [];
|
|
2515
|
-
for (const
|
|
2516
|
-
|
|
2517
|
-
if (
|
|
2518
|
-
if (
|
|
2519
|
-
|
|
2520
|
-
continue;
|
|
2521
|
-
|
|
2578
|
+
for (const statement of sourceFile.getStatements()) {
|
|
2579
|
+
const kind = statement.getKind();
|
|
2580
|
+
if (kind === SyntaxKind3.ImportDeclaration) continue;
|
|
2581
|
+
if (kind === SyntaxKind3.ExportDeclaration) {
|
|
2582
|
+
const exportDecl = statement.asKindOrThrow(SyntaxKind3.ExportDeclaration);
|
|
2583
|
+
if (exportDecl.getModuleSpecifier() !== void 0) continue;
|
|
2584
|
+
}
|
|
2585
|
+
violations.push(statement);
|
|
2522
2586
|
}
|
|
2523
2587
|
return violations;
|
|
2524
2588
|
}
|
|
2525
|
-
async function checkEnumBundleRules(cwd) {
|
|
2589
|
+
async function checkEnumBundleRules(cwd, project) {
|
|
2526
2590
|
const results = [];
|
|
2527
2591
|
const files = await fg9("src/**/*.enum-bundle.ts", {
|
|
2528
2592
|
cwd,
|
|
@@ -2531,9 +2595,9 @@ async function checkEnumBundleRules(cwd) {
|
|
|
2531
2595
|
for (const file of files) {
|
|
2532
2596
|
const fullPath = `${cwd}/${file}`;
|
|
2533
2597
|
const content = readFileContent(fullPath);
|
|
2534
|
-
const
|
|
2535
|
-
if (!
|
|
2536
|
-
const exportedNames = getExportedNames(
|
|
2598
|
+
const sourceFile = parseFile(fullPath, project);
|
|
2599
|
+
if (!sourceFile) continue;
|
|
2600
|
+
const exportedNames = getExportedNames(sourceFile);
|
|
2537
2601
|
const fileName = basename5(file, ".enum-bundle.ts");
|
|
2538
2602
|
const upperName = fileName.replace(/[^a-zA-Z0-9]/g, "_").toUpperCase();
|
|
2539
2603
|
const hasSchema = exportedNames.some((n) => n.endsWith("Schema"));
|
|
@@ -2575,7 +2639,7 @@ async function checkEnumBundleRules(cwd) {
|
|
|
2575
2639
|
}
|
|
2576
2640
|
return results;
|
|
2577
2641
|
}
|
|
2578
|
-
async function checkResponseRules(cwd) {
|
|
2642
|
+
async function checkResponseRules(cwd, project) {
|
|
2579
2643
|
const results = [];
|
|
2580
2644
|
const files = await fg9("src/**/*.response.ts", {
|
|
2581
2645
|
cwd,
|
|
@@ -2584,9 +2648,9 @@ async function checkResponseRules(cwd) {
|
|
|
2584
2648
|
for (const file of files) {
|
|
2585
2649
|
const fullPath = `${cwd}/${file}`;
|
|
2586
2650
|
const content = readFileContent(fullPath);
|
|
2587
|
-
const
|
|
2588
|
-
if (!
|
|
2589
|
-
const exportedNames = getExportedNames(
|
|
2651
|
+
const sourceFile = parseFile(fullPath, project);
|
|
2652
|
+
if (!sourceFile) continue;
|
|
2653
|
+
const exportedNames = getExportedNames(sourceFile);
|
|
2590
2654
|
const fileName = basename5(file, ".response.ts");
|
|
2591
2655
|
const httpCode = fileName.match(/^(\d{3})$/)?.[1];
|
|
2592
2656
|
if (httpCode) {
|
|
@@ -2600,7 +2664,7 @@ async function checkResponseRules(cwd) {
|
|
|
2600
2664
|
});
|
|
2601
2665
|
}
|
|
2602
2666
|
}
|
|
2603
|
-
if (!hasDefaultExport(
|
|
2667
|
+
if (!hasDefaultExport(sourceFile)) {
|
|
2604
2668
|
results.push({
|
|
2605
2669
|
ruleId: "CTR-RESPONSE-002",
|
|
2606
2670
|
severity: "error",
|
|
@@ -2621,7 +2685,7 @@ async function checkResponseRules(cwd) {
|
|
|
2621
2685
|
}
|
|
2622
2686
|
return results;
|
|
2623
2687
|
}
|
|
2624
|
-
async function checkRequestRules(cwd) {
|
|
2688
|
+
async function checkRequestRules(cwd, project) {
|
|
2625
2689
|
const results = [];
|
|
2626
2690
|
const files = await fg9("src/**/*.request.ts", {
|
|
2627
2691
|
cwd,
|
|
@@ -2630,9 +2694,9 @@ async function checkRequestRules(cwd) {
|
|
|
2630
2694
|
for (const file of files) {
|
|
2631
2695
|
const fullPath = `${cwd}/${file}`;
|
|
2632
2696
|
const content = readFileContent(fullPath);
|
|
2633
|
-
const
|
|
2634
|
-
if (!
|
|
2635
|
-
const exportedNames = getExportedNames(
|
|
2697
|
+
const sourceFile = parseFile(fullPath, project);
|
|
2698
|
+
if (!sourceFile) continue;
|
|
2699
|
+
const exportedNames = getExportedNames(sourceFile);
|
|
2636
2700
|
const hasTypedSchema = exportedNames.some(
|
|
2637
2701
|
(n) => /(Query|Body|Headers|Params)Schema$/.test(n)
|
|
2638
2702
|
);
|
|
@@ -2657,7 +2721,7 @@ async function checkRequestRules(cwd) {
|
|
|
2657
2721
|
}
|
|
2658
2722
|
return results;
|
|
2659
2723
|
}
|
|
2660
|
-
async function checkContractFileRules(cwd) {
|
|
2724
|
+
async function checkContractFileRules(cwd, project) {
|
|
2661
2725
|
const results = [];
|
|
2662
2726
|
const files = await fg9("src/**/*.contract.ts", {
|
|
2663
2727
|
cwd,
|
|
@@ -2666,9 +2730,9 @@ async function checkContractFileRules(cwd) {
|
|
|
2666
2730
|
for (const file of files) {
|
|
2667
2731
|
const fullPath = `${cwd}/${file}`;
|
|
2668
2732
|
const content = readFileContent(fullPath);
|
|
2669
|
-
const
|
|
2670
|
-
if (!
|
|
2671
|
-
const exportedNames = getExportedNames(
|
|
2733
|
+
const sourceFile = parseFile(fullPath, project);
|
|
2734
|
+
if (!sourceFile) continue;
|
|
2735
|
+
const exportedNames = getExportedNames(sourceFile);
|
|
2672
2736
|
const hasContractExport = exportedNames.some((n) => n.endsWith("Contract"));
|
|
2673
2737
|
if (!hasContractExport) {
|
|
2674
2738
|
results.push({
|
|
@@ -2700,7 +2764,7 @@ async function checkContractFileRules(cwd) {
|
|
|
2700
2764
|
}
|
|
2701
2765
|
return results;
|
|
2702
2766
|
}
|
|
2703
|
-
async function checkIndexRules(cwd) {
|
|
2767
|
+
async function checkIndexRules(cwd, project) {
|
|
2704
2768
|
const results = [];
|
|
2705
2769
|
const files = await fg9("src/**/index.ts", {
|
|
2706
2770
|
cwd,
|
|
@@ -2708,16 +2772,16 @@ async function checkIndexRules(cwd) {
|
|
|
2708
2772
|
});
|
|
2709
2773
|
for (const file of files) {
|
|
2710
2774
|
const fullPath = `${cwd}/${file}`;
|
|
2711
|
-
const
|
|
2712
|
-
if (!
|
|
2713
|
-
const violations = getNonReexportStatements(
|
|
2775
|
+
const sourceFile = parseFile(fullPath, project);
|
|
2776
|
+
if (!sourceFile) continue;
|
|
2777
|
+
const violations = getNonReexportStatements(sourceFile);
|
|
2714
2778
|
if (violations.length > 0) {
|
|
2715
2779
|
results.push({
|
|
2716
2780
|
ruleId: "CTR-INDEX-001",
|
|
2717
2781
|
severity: "error",
|
|
2718
2782
|
message: `Index file ${file} contains ${violations.length} non-reexport statement(s)`,
|
|
2719
2783
|
file,
|
|
2720
|
-
line: violations[0]?.
|
|
2784
|
+
line: violations[0]?.getStartLineNumber(),
|
|
2721
2785
|
suggestion: "Index files should only contain: export * from '...' or export { X } from '...'"
|
|
2722
2786
|
});
|
|
2723
2787
|
}
|
|
@@ -2774,7 +2838,7 @@ async function checkNamingRules(cwd) {
|
|
|
2774
2838
|
}
|
|
2775
2839
|
return results;
|
|
2776
2840
|
}
|
|
2777
|
-
async function checkDeprecatedRules(cwd) {
|
|
2841
|
+
async function checkDeprecatedRules(cwd, project) {
|
|
2778
2842
|
const results = [];
|
|
2779
2843
|
const files = await fg9("src/**/*.deprecated.ts", {
|
|
2780
2844
|
cwd,
|
|
@@ -2782,10 +2846,9 @@ async function checkDeprecatedRules(cwd) {
|
|
|
2782
2846
|
});
|
|
2783
2847
|
for (const file of files) {
|
|
2784
2848
|
const fullPath = `${cwd}/${file}`;
|
|
2785
|
-
const
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
const missing = getExportsWithoutDeprecatedJsdoc(ast, content);
|
|
2849
|
+
const sourceFile = parseFile(fullPath, project);
|
|
2850
|
+
if (!sourceFile) continue;
|
|
2851
|
+
const missing = getExportsWithoutDeprecatedJsdoc(sourceFile);
|
|
2789
2852
|
if (missing.length > 0) {
|
|
2790
2853
|
results.push({
|
|
2791
2854
|
ruleId: "CTR-DEPRECATED-001",
|
|
@@ -2824,6 +2887,7 @@ var contractsNomenclatureChecker = {
|
|
|
2824
2887
|
}
|
|
2825
2888
|
const disabledRules = getDisabledRules(configResult.config);
|
|
2826
2889
|
const results = [];
|
|
2890
|
+
const project = createProject();
|
|
2827
2891
|
const [
|
|
2828
2892
|
enumResults,
|
|
2829
2893
|
responseResults,
|
|
@@ -2834,14 +2898,14 @@ var contractsNomenclatureChecker = {
|
|
|
2834
2898
|
namingResults,
|
|
2835
2899
|
deprecatedResults
|
|
2836
2900
|
] = await Promise.all([
|
|
2837
|
-
checkEnumBundleRules(ctx.cwd),
|
|
2838
|
-
checkResponseRules(ctx.cwd),
|
|
2839
|
-
checkRequestRules(ctx.cwd),
|
|
2840
|
-
checkContractFileRules(ctx.cwd),
|
|
2841
|
-
checkIndexRules(ctx.cwd),
|
|
2901
|
+
checkEnumBundleRules(ctx.cwd, project),
|
|
2902
|
+
checkResponseRules(ctx.cwd, project),
|
|
2903
|
+
checkRequestRules(ctx.cwd, project),
|
|
2904
|
+
checkContractFileRules(ctx.cwd, project),
|
|
2905
|
+
checkIndexRules(ctx.cwd, project),
|
|
2842
2906
|
checkHelperRules(ctx.cwd),
|
|
2843
2907
|
checkNamingRules(ctx.cwd),
|
|
2844
|
-
checkDeprecatedRules(ctx.cwd)
|
|
2908
|
+
checkDeprecatedRules(ctx.cwd, project)
|
|
2845
2909
|
]);
|
|
2846
2910
|
results.push(
|
|
2847
2911
|
...enumResults,
|
|
@@ -2948,7 +3012,7 @@ var contractsChecker = {
|
|
|
2948
3012
|
// src/lint/checkers/vault/index.ts
|
|
2949
3013
|
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
2950
3014
|
import { join as join3, dirname as dirname4 } from "path";
|
|
2951
|
-
import { Project, SyntaxKind } from "ts-morph";
|
|
3015
|
+
import { Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
2952
3016
|
var VAULT_PATHS = [
|
|
2953
3017
|
"src/common/config/vault",
|
|
2954
3018
|
"server/vault",
|
|
@@ -2977,7 +3041,7 @@ function isInsideZodObject(node) {
|
|
|
2977
3041
|
if (!node) return false;
|
|
2978
3042
|
let current = node;
|
|
2979
3043
|
while (current) {
|
|
2980
|
-
if (current.getKind() ===
|
|
3044
|
+
if (current.getKind() === SyntaxKind4.CallExpression) {
|
|
2981
3045
|
const text = current.getText();
|
|
2982
3046
|
if (text.startsWith("z.object(")) return true;
|
|
2983
3047
|
}
|
|
@@ -2989,7 +3053,7 @@ function checkAstPatterns(sourceFile) {
|
|
|
2989
3053
|
const results = [];
|
|
2990
3054
|
const filePath = sourceFile.getFilePath();
|
|
2991
3055
|
sourceFile.forEachDescendant((node) => {
|
|
2992
|
-
if (node.getKind() ===
|
|
3056
|
+
if (node.getKind() === SyntaxKind4.CallExpression) {
|
|
2993
3057
|
const text = node.getText();
|
|
2994
3058
|
if (text.startsWith("z.lazy(")) {
|
|
2995
3059
|
results.push({
|
|
@@ -3001,11 +3065,11 @@ function checkAstPatterns(sourceFile) {
|
|
|
3001
3065
|
});
|
|
3002
3066
|
}
|
|
3003
3067
|
if (text.startsWith("z.enum(")) {
|
|
3004
|
-
const callExpr = node.asKind(
|
|
3068
|
+
const callExpr = node.asKind(SyntaxKind4.CallExpression);
|
|
3005
3069
|
const args = callExpr?.getArguments();
|
|
3006
3070
|
if (args && args.length > 0) {
|
|
3007
3071
|
const firstArg = args[0];
|
|
3008
|
-
if (firstArg?.getKind() ===
|
|
3072
|
+
if (firstArg?.getKind() === SyntaxKind4.Identifier) {
|
|
3009
3073
|
results.push({
|
|
3010
3074
|
ruleId: "VAULT-AST-004",
|
|
3011
3075
|
severity: getRuleSeverity("VAULT-AST-004"),
|
|
@@ -3018,11 +3082,11 @@ function checkAstPatterns(sourceFile) {
|
|
|
3018
3082
|
}
|
|
3019
3083
|
}
|
|
3020
3084
|
if (text.startsWith("z.object(")) {
|
|
3021
|
-
const callExpr = node.asKind(
|
|
3085
|
+
const callExpr = node.asKind(SyntaxKind4.CallExpression);
|
|
3022
3086
|
const args = callExpr?.getArguments();
|
|
3023
3087
|
if (args && args.length > 0) {
|
|
3024
3088
|
const firstArg = args[0];
|
|
3025
|
-
if (firstArg?.getKind() ===
|
|
3089
|
+
if (firstArg?.getKind() === SyntaxKind4.Identifier) {
|
|
3026
3090
|
results.push({
|
|
3027
3091
|
ruleId: "VAULT-AST-006",
|
|
3028
3092
|
severity: getRuleSeverity("VAULT-AST-006"),
|
|
@@ -3034,11 +3098,11 @@ function checkAstPatterns(sourceFile) {
|
|
|
3034
3098
|
}
|
|
3035
3099
|
}
|
|
3036
3100
|
}
|
|
3037
|
-
if (node.getKind() ===
|
|
3101
|
+
if (node.getKind() === SyntaxKind4.SpreadAssignment) {
|
|
3038
3102
|
if (isInsideZodObject(node)) {
|
|
3039
|
-
const spreadExpr = node.asKind(
|
|
3103
|
+
const spreadExpr = node.asKind(SyntaxKind4.SpreadAssignment);
|
|
3040
3104
|
const expr = spreadExpr?.getExpression();
|
|
3041
|
-
if (expr?.getKind() ===
|
|
3105
|
+
if (expr?.getKind() === SyntaxKind4.ConditionalExpression || expr?.getKind() === SyntaxKind4.Identifier) {
|
|
3042
3106
|
results.push({
|
|
3043
3107
|
ruleId: "VAULT-AST-002",
|
|
3044
3108
|
severity: getRuleSeverity("VAULT-AST-002"),
|
|
@@ -3049,8 +3113,8 @@ function checkAstPatterns(sourceFile) {
|
|
|
3049
3113
|
}
|
|
3050
3114
|
}
|
|
3051
3115
|
}
|
|
3052
|
-
if (node.getKind() ===
|
|
3053
|
-
const funcNode = node.asKind(
|
|
3116
|
+
if (node.getKind() === SyntaxKind4.FunctionDeclaration || node.getKind() === SyntaxKind4.ArrowFunction) {
|
|
3117
|
+
const funcNode = node.asKind(SyntaxKind4.FunctionDeclaration) || node.asKind(SyntaxKind4.ArrowFunction);
|
|
3054
3118
|
if (funcNode) {
|
|
3055
3119
|
const body = funcNode.getBody();
|
|
3056
3120
|
if (body) {
|
|
@@ -3082,7 +3146,7 @@ var vaultChecker = {
|
|
|
3082
3146
|
}
|
|
3083
3147
|
const configResult = loadConfig(ctx.cwd);
|
|
3084
3148
|
const disabledRules = configResult.success ? getDisabledVaultRules(configResult.config) : [];
|
|
3085
|
-
const project = new
|
|
3149
|
+
const project = new Project4({
|
|
3086
3150
|
skipAddingFilesFromTsConfig: true,
|
|
3087
3151
|
skipFileDependencyResolution: true
|
|
3088
3152
|
});
|
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
lintCommand,
|
|
6
6
|
loadConfig,
|
|
7
7
|
scaffoldCommand
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-73G2DONK.js";
|
|
9
9
|
import {
|
|
10
10
|
scanForVaultRepos
|
|
11
11
|
} from "./chunk-WXFSGE4N.js";
|
|
@@ -21,7 +21,7 @@ import { resolve } from "path";
|
|
|
21
21
|
// src/lib/contracts/codemod/analyze-bff.ts
|
|
22
22
|
import { readFileSync } from "fs";
|
|
23
23
|
import fg from "fast-glob";
|
|
24
|
-
import {
|
|
24
|
+
import { Project, SyntaxKind } from "ts-morph";
|
|
25
25
|
var MODULE_TO_SERVICE = {
|
|
26
26
|
SeeCoreModule: "seeCore",
|
|
27
27
|
LocusModule: "locus",
|
|
@@ -104,63 +104,48 @@ function extractApiServicesFromModule(modulePath) {
|
|
|
104
104
|
return services;
|
|
105
105
|
}
|
|
106
106
|
function analyzeController(controllerPath, apiServices) {
|
|
107
|
-
const content = readFileSync(controllerPath, "utf-8");
|
|
108
107
|
const mappings = [];
|
|
109
|
-
|
|
108
|
+
const project = new Project({ skipAddingFilesFromTsConfig: true });
|
|
109
|
+
let sourceFile;
|
|
110
110
|
try {
|
|
111
|
-
|
|
111
|
+
sourceFile = project.addSourceFileAtPath(controllerPath);
|
|
112
112
|
} catch {
|
|
113
113
|
return mappings;
|
|
114
114
|
}
|
|
115
|
-
for (const
|
|
116
|
-
|
|
117
|
-
const classNode = node.declaration;
|
|
118
|
-
analyzeClassMethods(
|
|
119
|
-
classNode,
|
|
120
|
-
controllerPath,
|
|
121
|
-
apiServices,
|
|
122
|
-
content,
|
|
123
|
-
mappings
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
if (node.type === "ClassDeclaration") {
|
|
127
|
-
analyzeClassMethods(node, controllerPath, apiServices, content, mappings);
|
|
128
|
-
}
|
|
115
|
+
for (const classDecl of sourceFile.getClasses()) {
|
|
116
|
+
analyzeClassMethods(classDecl, controllerPath, apiServices, mappings);
|
|
129
117
|
}
|
|
130
118
|
return mappings;
|
|
131
119
|
}
|
|
132
|
-
function analyzeClassMethods(
|
|
133
|
-
for (const
|
|
134
|
-
|
|
135
|
-
if (member.key.type !== "Identifier") continue;
|
|
136
|
-
const methodName = member.key.name;
|
|
120
|
+
function analyzeClassMethods(classDecl, controllerPath, apiServices, mappings) {
|
|
121
|
+
for (const method of classDecl.getMethods()) {
|
|
122
|
+
const methodName = method.getName();
|
|
137
123
|
if (methodName === "constructor" || methodName.startsWith("_")) continue;
|
|
138
|
-
const decorators = member.decorators ?? [];
|
|
139
124
|
let permission = null;
|
|
140
|
-
let
|
|
125
|
+
let httpMethod = null;
|
|
141
126
|
let path = null;
|
|
142
127
|
let contractEndpointName = null;
|
|
143
|
-
for (const decorator of
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
128
|
+
for (const decorator of method.getDecorators()) {
|
|
129
|
+
const decoratorName = decorator.getName();
|
|
130
|
+
const args = decorator.getArguments();
|
|
131
|
+
if (decoratorName === "TsRestHandler" && args[0]) {
|
|
132
|
+
const arg = args[0];
|
|
133
|
+
if (arg.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
134
|
+
const propAccess = arg.asKind(SyntaxKind.PropertyAccessExpression);
|
|
135
|
+
contractEndpointName = propAccess.getName();
|
|
151
136
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
137
|
+
}
|
|
138
|
+
if (decoratorName === "Authorize" && args[0]) {
|
|
139
|
+
const arg = args[0];
|
|
140
|
+
if (arg.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
141
|
+
const propAccess = arg.asKind(SyntaxKind.PropertyAccessExpression);
|
|
142
|
+
permission = propAccess.getName();
|
|
157
143
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
144
|
+
}
|
|
145
|
+
if (["Get", "Post", "Put", "Patch", "Delete"].includes(decoratorName)) {
|
|
146
|
+
httpMethod = decoratorName.toUpperCase();
|
|
147
|
+
if (args[0]?.getKind() === SyntaxKind.StringLiteral) {
|
|
148
|
+
path = args[0].asKind(SyntaxKind.StringLiteral).getLiteralValue();
|
|
164
149
|
}
|
|
165
150
|
}
|
|
166
151
|
}
|
|
@@ -174,7 +159,7 @@ function analyzeClassMethods(classNode, controllerPath, apiServices, content, ma
|
|
|
174
159
|
permissionValue,
|
|
175
160
|
apiModules,
|
|
176
161
|
apiServices,
|
|
177
|
-
method,
|
|
162
|
+
method: httpMethod,
|
|
178
163
|
path
|
|
179
164
|
});
|
|
180
165
|
}
|
package/dist/mcp-server.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyphaene/hexa-ts-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "TypeScript dev kit for Claude Code agents: architecture linting, scaffolding, knowledge analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -47,9 +47,7 @@
|
|
|
47
47
|
"homepage": "https://github.com/hyphaene/hexa-ts-kit#readme",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
50
|
-
"
|
|
51
|
-
"@typescript-eslint/typescript-estree": "^8.50.1",
|
|
52
|
-
"commander": "^12.1.0",
|
|
50
|
+
"commander": "^14.0.3",
|
|
53
51
|
"fast-glob": "^3.3.2",
|
|
54
52
|
"gray-matter": "^4.0.3",
|
|
55
53
|
"ink": "^6.6.0",
|
|
@@ -60,20 +58,19 @@
|
|
|
60
58
|
"picocolors": "^1.1.1",
|
|
61
59
|
"react": "^19.2.4",
|
|
62
60
|
"ts-morph": "^27.0.2",
|
|
63
|
-
"vue-eslint-parser": "^10.2.0",
|
|
64
61
|
"yaml": "^2.8.2",
|
|
65
62
|
"zod": "^4.3.4"
|
|
66
63
|
},
|
|
67
64
|
"devDependencies": {
|
|
68
65
|
"@semantic-release/changelog": "^6.0.3",
|
|
69
66
|
"@semantic-release/git": "^10.0.1",
|
|
70
|
-
"@types/node": "^
|
|
67
|
+
"@types/node": "^25.2.3",
|
|
71
68
|
"@types/react": "^19.2.10",
|
|
72
|
-
"@vitest/coverage-v8": "^
|
|
69
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
73
70
|
"semantic-release": "^25.0.2",
|
|
74
71
|
"tsup": "^8.3.5",
|
|
75
72
|
"typescript": "^5.7.2",
|
|
76
|
-
"vitest": "^
|
|
73
|
+
"vitest": "^4.0.18"
|
|
77
74
|
},
|
|
78
75
|
"engines": {
|
|
79
76
|
"node": ">=20"
|