@hyphaene/hexa-ts-kit 1.12.0 → 1.12.2
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-TGZA3GHG.js → chunk-4LVIIP2D.js} +339 -255
- package/dist/cli.js +31 -46
- package/dist/mcp-server.js +1 -1
- package/package.json +1 -4
|
@@ -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",
|
|
@@ -2411,8 +2445,12 @@ var ALLOWED_SUFFIXES = [
|
|
|
2411
2445
|
var ALLOWED_SPECIAL_FILES = ["index.ts", "openapi.ts"];
|
|
2412
2446
|
function parseFile(filePath) {
|
|
2413
2447
|
try {
|
|
2414
|
-
const
|
|
2415
|
-
|
|
2448
|
+
const project = new Project3({
|
|
2449
|
+
useInMemoryFileSystem: false,
|
|
2450
|
+
skipAddingFilesFromTsConfig: true
|
|
2451
|
+
});
|
|
2452
|
+
project.addSourceFileAtPath(filePath);
|
|
2453
|
+
return project.getSourceFileOrThrow(filePath);
|
|
2416
2454
|
} catch {
|
|
2417
2455
|
return null;
|
|
2418
2456
|
}
|
|
@@ -2420,42 +2458,58 @@ function parseFile(filePath) {
|
|
|
2420
2458
|
function readFileContent(filePath) {
|
|
2421
2459
|
return readFileSync4(filePath, "utf-8");
|
|
2422
2460
|
}
|
|
2423
|
-
function getExportedNames(
|
|
2461
|
+
function getExportedNames(sourceFile) {
|
|
2424
2462
|
const names = [];
|
|
2425
|
-
for (const
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2463
|
+
for (const statement of sourceFile.getStatements()) {
|
|
2464
|
+
const kind = statement.getKind();
|
|
2465
|
+
if (kind === SyntaxKind3.ExportDeclaration) {
|
|
2466
|
+
const exportDecl = statement.asKindOrThrow(SyntaxKind3.ExportDeclaration);
|
|
2467
|
+
for (const namedExport of exportDecl.getNamedExports()) {
|
|
2468
|
+
names.push(
|
|
2469
|
+
namedExport.getAliasNode()?.getText() ?? namedExport.getName()
|
|
2470
|
+
);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
if (Node.isModifierable(statement) && statement.hasModifier(SyntaxKind3.ExportKeyword)) {
|
|
2474
|
+
if (kind === SyntaxKind3.VariableStatement) {
|
|
2475
|
+
const varStatement = statement.asKindOrThrow(
|
|
2476
|
+
SyntaxKind3.VariableStatement
|
|
2477
|
+
);
|
|
2478
|
+
for (const decl of varStatement.getDeclarations()) {
|
|
2479
|
+
const nameNode = decl.getNameNode();
|
|
2480
|
+
if (nameNode.getKind() === SyntaxKind3.Identifier) {
|
|
2481
|
+
names.push(nameNode.getText());
|
|
2482
|
+
} else if (nameNode.getKind() === SyntaxKind3.ObjectBindingPattern) {
|
|
2483
|
+
const objectPattern = nameNode.asKindOrThrow(
|
|
2484
|
+
SyntaxKind3.ObjectBindingPattern
|
|
2485
|
+
);
|
|
2486
|
+
for (const element of objectPattern.getElements()) {
|
|
2487
|
+
const propertyName = element.getPropertyNameNode();
|
|
2488
|
+
const elementName = element.getNameNode();
|
|
2489
|
+
names.push(elementName.getText());
|
|
2432
2490
|
}
|
|
2433
2491
|
}
|
|
2434
2492
|
}
|
|
2435
|
-
if (node.declaration.type === "TSTypeAliasDeclaration") {
|
|
2436
|
-
names.push(node.declaration.id.name);
|
|
2437
|
-
}
|
|
2438
|
-
if (node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
|
|
2439
|
-
names.push(node.declaration.id.name);
|
|
2440
|
-
}
|
|
2441
2493
|
}
|
|
2442
|
-
if (
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2494
|
+
if (kind === SyntaxKind3.TypeAliasDeclaration) {
|
|
2495
|
+
const typeAlias = statement.asKindOrThrow(
|
|
2496
|
+
SyntaxKind3.TypeAliasDeclaration
|
|
2497
|
+
);
|
|
2498
|
+
names.push(typeAlias.getName());
|
|
2499
|
+
}
|
|
2500
|
+
if (kind === SyntaxKind3.FunctionDeclaration) {
|
|
2501
|
+
const funcDecl = statement.asKindOrThrow(
|
|
2502
|
+
SyntaxKind3.FunctionDeclaration
|
|
2503
|
+
);
|
|
2504
|
+
const funcName = funcDecl.getName();
|
|
2505
|
+
if (funcName) names.push(funcName);
|
|
2448
2506
|
}
|
|
2449
2507
|
}
|
|
2450
2508
|
}
|
|
2451
2509
|
return names;
|
|
2452
2510
|
}
|
|
2453
|
-
function hasDefaultExport(
|
|
2454
|
-
return
|
|
2455
|
-
(node) => node.type === "ExportDefaultDeclaration" || node.type === "ExportNamedDeclaration" && node.specifiers?.some(
|
|
2456
|
-
(s) => s.exported.type === "Identifier" && s.exported.name === "default"
|
|
2457
|
-
)
|
|
2458
|
-
);
|
|
2511
|
+
function hasDefaultExport(sourceFile) {
|
|
2512
|
+
return sourceFile.getExportedDeclarations().has("default");
|
|
2459
2513
|
}
|
|
2460
2514
|
function hasOpenApiDocumentation(content) {
|
|
2461
2515
|
return /\.describe\s*\(/.test(content) || /\.openapi\s*\(/.test(content);
|
|
@@ -2467,44 +2521,66 @@ function hasZodInferTypeExport(content) {
|
|
|
2467
2521
|
return /export\s+type\s+\w+\s*=\s*z\.infer/.test(content);
|
|
2468
2522
|
}
|
|
2469
2523
|
function hasZodDataSchemas(content) {
|
|
2470
|
-
return /(?:const|let|var)\s+\w+\s*=\s*z\.(?:object|enum|array)\s*\(/.test(
|
|
2524
|
+
return /(?:const|let|var)\s+\w+\s*=\s*z\.(?:object|enum|array)\s*\(/.test(
|
|
2525
|
+
content
|
|
2526
|
+
);
|
|
2471
2527
|
}
|
|
2472
|
-
function getExportsWithoutDeprecatedJsdoc(
|
|
2528
|
+
function getExportsWithoutDeprecatedJsdoc(sourceFile) {
|
|
2473
2529
|
const missingDeprecated = [];
|
|
2474
|
-
for (const
|
|
2475
|
-
|
|
2530
|
+
for (const statement of sourceFile.getStatements()) {
|
|
2531
|
+
const kind = statement.getKind();
|
|
2532
|
+
const isExported = Node.isModifierable(statement) && statement.hasModifier(SyntaxKind3.ExportKeyword);
|
|
2533
|
+
if (kind !== SyntaxKind3.ExportDeclaration && kind !== SyntaxKind3.ExportAssignment && !isExported) {
|
|
2476
2534
|
continue;
|
|
2477
2535
|
}
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
if (node.declaration.type === "VariableDeclaration") {
|
|
2482
|
-
const decl = node.declaration.declarations[0];
|
|
2483
|
-
if (decl?.id.type === "Identifier") {
|
|
2484
|
-
exportName = decl.id.name;
|
|
2485
|
-
}
|
|
2486
|
-
} else if (node.declaration.type === "TSTypeAliasDeclaration" || node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
|
|
2487
|
-
exportName = node.declaration.type === "TSTypeAliasDeclaration" ? node.declaration.id.name : node.declaration.id.name;
|
|
2488
|
-
}
|
|
2536
|
+
if (kind === SyntaxKind3.ExportAssignment) {
|
|
2537
|
+
if (!hasDeprecatedComment(statement)) {
|
|
2538
|
+
missingDeprecated.push("default");
|
|
2489
2539
|
}
|
|
2490
|
-
|
|
2540
|
+
continue;
|
|
2491
2541
|
}
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2542
|
+
if (kind === SyntaxKind3.ExportDeclaration) {
|
|
2543
|
+
const exportDecl = statement.asKindOrThrow(SyntaxKind3.ExportDeclaration);
|
|
2544
|
+
if (exportDecl.getModuleSpecifier() !== void 0) continue;
|
|
2545
|
+
}
|
|
2546
|
+
let exportName = "unknown";
|
|
2547
|
+
if (kind === SyntaxKind3.VariableStatement) {
|
|
2548
|
+
const varStatement = statement.asKindOrThrow(
|
|
2549
|
+
SyntaxKind3.VariableStatement
|
|
2550
|
+
);
|
|
2551
|
+
const declarations = varStatement.getDeclarations();
|
|
2552
|
+
const firstDecl = declarations[0];
|
|
2553
|
+
if (firstDecl) {
|
|
2554
|
+
exportName = firstDecl.getName();
|
|
2555
|
+
}
|
|
2556
|
+
} else if (kind === SyntaxKind3.TypeAliasDeclaration) {
|
|
2557
|
+
const typeAlias = statement.asKindOrThrow(
|
|
2558
|
+
SyntaxKind3.TypeAliasDeclaration
|
|
2559
|
+
);
|
|
2560
|
+
exportName = typeAlias.getName();
|
|
2561
|
+
} else if (kind === SyntaxKind3.FunctionDeclaration) {
|
|
2562
|
+
const funcDecl = statement.asKindOrThrow(SyntaxKind3.FunctionDeclaration);
|
|
2563
|
+
exportName = funcDecl.getName() ?? "anonymous";
|
|
2564
|
+
}
|
|
2565
|
+
if (!hasDeprecatedComment(statement)) {
|
|
2495
2566
|
missingDeprecated.push(exportName);
|
|
2496
2567
|
}
|
|
2497
2568
|
}
|
|
2498
2569
|
return missingDeprecated;
|
|
2499
2570
|
}
|
|
2500
|
-
function
|
|
2571
|
+
function hasDeprecatedComment(node) {
|
|
2572
|
+
return node.getLeadingCommentRanges().some((comment) => comment.getText().includes("@deprecated"));
|
|
2573
|
+
}
|
|
2574
|
+
function getNonReexportStatements(sourceFile) {
|
|
2501
2575
|
const violations = [];
|
|
2502
|
-
for (const
|
|
2503
|
-
|
|
2504
|
-
if (
|
|
2505
|
-
if (
|
|
2506
|
-
|
|
2507
|
-
|
|
2576
|
+
for (const statement of sourceFile.getStatements()) {
|
|
2577
|
+
const kind = statement.getKind();
|
|
2578
|
+
if (kind === SyntaxKind3.ImportDeclaration) continue;
|
|
2579
|
+
if (kind === SyntaxKind3.ExportDeclaration) {
|
|
2580
|
+
const exportDecl = statement.asKindOrThrow(SyntaxKind3.ExportDeclaration);
|
|
2581
|
+
if (exportDecl.getModuleSpecifier() !== void 0) continue;
|
|
2582
|
+
}
|
|
2583
|
+
violations.push(statement);
|
|
2508
2584
|
}
|
|
2509
2585
|
return violations;
|
|
2510
2586
|
}
|
|
@@ -2517,13 +2593,15 @@ async function checkEnumBundleRules(cwd) {
|
|
|
2517
2593
|
for (const file of files) {
|
|
2518
2594
|
const fullPath = `${cwd}/${file}`;
|
|
2519
2595
|
const content = readFileContent(fullPath);
|
|
2520
|
-
const
|
|
2521
|
-
if (!
|
|
2522
|
-
const exportedNames = getExportedNames(
|
|
2596
|
+
const sourceFile = parseFile(fullPath);
|
|
2597
|
+
if (!sourceFile) continue;
|
|
2598
|
+
const exportedNames = getExportedNames(sourceFile);
|
|
2523
2599
|
const fileName = basename5(file, ".enum-bundle.ts");
|
|
2524
2600
|
const upperName = fileName.replace(/[^a-zA-Z0-9]/g, "_").toUpperCase();
|
|
2525
2601
|
const hasSchema = exportedNames.some((n) => n.endsWith("Schema"));
|
|
2526
|
-
const hasEnumMapper = exportedNames.some(
|
|
2602
|
+
const hasEnumMapper = exportedNames.some(
|
|
2603
|
+
(n) => n === n.toUpperCase() && n.length > 1
|
|
2604
|
+
);
|
|
2527
2605
|
const hasValues = exportedNames.some((n) => n.endsWith("_VALUES"));
|
|
2528
2606
|
if (!hasSchema || !hasEnumMapper || !hasValues) {
|
|
2529
2607
|
const missing = [];
|
|
@@ -2568,9 +2646,9 @@ async function checkResponseRules(cwd) {
|
|
|
2568
2646
|
for (const file of files) {
|
|
2569
2647
|
const fullPath = `${cwd}/${file}`;
|
|
2570
2648
|
const content = readFileContent(fullPath);
|
|
2571
|
-
const
|
|
2572
|
-
if (!
|
|
2573
|
-
const exportedNames = getExportedNames(
|
|
2649
|
+
const sourceFile = parseFile(fullPath);
|
|
2650
|
+
if (!sourceFile) continue;
|
|
2651
|
+
const exportedNames = getExportedNames(sourceFile);
|
|
2574
2652
|
const fileName = basename5(file, ".response.ts");
|
|
2575
2653
|
const httpCode = fileName.match(/^(\d{3})$/)?.[1];
|
|
2576
2654
|
if (httpCode) {
|
|
@@ -2584,7 +2662,7 @@ async function checkResponseRules(cwd) {
|
|
|
2584
2662
|
});
|
|
2585
2663
|
}
|
|
2586
2664
|
}
|
|
2587
|
-
if (!hasDefaultExport(
|
|
2665
|
+
if (!hasDefaultExport(sourceFile)) {
|
|
2588
2666
|
results.push({
|
|
2589
2667
|
ruleId: "CTR-RESPONSE-002",
|
|
2590
2668
|
severity: "error",
|
|
@@ -2614,9 +2692,9 @@ async function checkRequestRules(cwd) {
|
|
|
2614
2692
|
for (const file of files) {
|
|
2615
2693
|
const fullPath = `${cwd}/${file}`;
|
|
2616
2694
|
const content = readFileContent(fullPath);
|
|
2617
|
-
const
|
|
2618
|
-
if (!
|
|
2619
|
-
const exportedNames = getExportedNames(
|
|
2695
|
+
const sourceFile = parseFile(fullPath);
|
|
2696
|
+
if (!sourceFile) continue;
|
|
2697
|
+
const exportedNames = getExportedNames(sourceFile);
|
|
2620
2698
|
const hasTypedSchema = exportedNames.some(
|
|
2621
2699
|
(n) => /(Query|Body|Headers|Params)Schema$/.test(n)
|
|
2622
2700
|
);
|
|
@@ -2650,9 +2728,9 @@ async function checkContractFileRules(cwd) {
|
|
|
2650
2728
|
for (const file of files) {
|
|
2651
2729
|
const fullPath = `${cwd}/${file}`;
|
|
2652
2730
|
const content = readFileContent(fullPath);
|
|
2653
|
-
const
|
|
2654
|
-
if (!
|
|
2655
|
-
const exportedNames = getExportedNames(
|
|
2731
|
+
const sourceFile = parseFile(fullPath);
|
|
2732
|
+
if (!sourceFile) continue;
|
|
2733
|
+
const exportedNames = getExportedNames(sourceFile);
|
|
2656
2734
|
const hasContractExport = exportedNames.some((n) => n.endsWith("Contract"));
|
|
2657
2735
|
if (!hasContractExport) {
|
|
2658
2736
|
results.push({
|
|
@@ -2692,16 +2770,16 @@ async function checkIndexRules(cwd) {
|
|
|
2692
2770
|
});
|
|
2693
2771
|
for (const file of files) {
|
|
2694
2772
|
const fullPath = `${cwd}/${file}`;
|
|
2695
|
-
const
|
|
2696
|
-
if (!
|
|
2697
|
-
const violations = getNonReexportStatements(
|
|
2773
|
+
const sourceFile = parseFile(fullPath);
|
|
2774
|
+
if (!sourceFile) continue;
|
|
2775
|
+
const violations = getNonReexportStatements(sourceFile);
|
|
2698
2776
|
if (violations.length > 0) {
|
|
2699
2777
|
results.push({
|
|
2700
2778
|
ruleId: "CTR-INDEX-001",
|
|
2701
2779
|
severity: "error",
|
|
2702
2780
|
message: `Index file ${file} contains ${violations.length} non-reexport statement(s)`,
|
|
2703
2781
|
file,
|
|
2704
|
-
line: violations[0]?.
|
|
2782
|
+
line: violations[0]?.getStartLineNumber(),
|
|
2705
2783
|
suggestion: "Index files should only contain: export * from '...' or export { X } from '...'"
|
|
2706
2784
|
});
|
|
2707
2785
|
}
|
|
@@ -2733,12 +2811,19 @@ async function checkNamingRules(cwd) {
|
|
|
2733
2811
|
const results = [];
|
|
2734
2812
|
const files = await fg9("src/**/*.ts", {
|
|
2735
2813
|
cwd,
|
|
2736
|
-
ignore: [
|
|
2814
|
+
ignore: [
|
|
2815
|
+
"**/node_modules/**",
|
|
2816
|
+
"**/dist/**",
|
|
2817
|
+
"**/lib/**",
|
|
2818
|
+
"**/__tests__/**"
|
|
2819
|
+
]
|
|
2737
2820
|
});
|
|
2738
2821
|
for (const file of files) {
|
|
2739
2822
|
const fileName = basename5(file);
|
|
2740
2823
|
if (ALLOWED_SPECIAL_FILES.includes(fileName)) continue;
|
|
2741
|
-
const matchesSuffix = ALLOWED_SUFFIXES.some(
|
|
2824
|
+
const matchesSuffix = ALLOWED_SUFFIXES.some(
|
|
2825
|
+
(suffix) => fileName.endsWith(suffix)
|
|
2826
|
+
);
|
|
2742
2827
|
if (!matchesSuffix) {
|
|
2743
2828
|
results.push({
|
|
2744
2829
|
ruleId: "CTR-NAMING-001",
|
|
@@ -2759,10 +2844,9 @@ async function checkDeprecatedRules(cwd) {
|
|
|
2759
2844
|
});
|
|
2760
2845
|
for (const file of files) {
|
|
2761
2846
|
const fullPath = `${cwd}/${file}`;
|
|
2762
|
-
const
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
const missing = getExportsWithoutDeprecatedJsdoc(ast, content);
|
|
2847
|
+
const sourceFile = parseFile(fullPath);
|
|
2848
|
+
if (!sourceFile) continue;
|
|
2849
|
+
const missing = getExportsWithoutDeprecatedJsdoc(sourceFile);
|
|
2766
2850
|
if (missing.length > 0) {
|
|
2767
2851
|
results.push({
|
|
2768
2852
|
ruleId: "CTR-DEPRECATED-001",
|
|
@@ -2925,7 +3009,7 @@ var contractsChecker = {
|
|
|
2925
3009
|
// src/lint/checkers/vault/index.ts
|
|
2926
3010
|
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
2927
3011
|
import { join as join3, dirname as dirname4 } from "path";
|
|
2928
|
-
import { Project, SyntaxKind } from "ts-morph";
|
|
3012
|
+
import { Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
2929
3013
|
var VAULT_PATHS = [
|
|
2930
3014
|
"src/common/config/vault",
|
|
2931
3015
|
"server/vault",
|
|
@@ -2954,7 +3038,7 @@ function isInsideZodObject(node) {
|
|
|
2954
3038
|
if (!node) return false;
|
|
2955
3039
|
let current = node;
|
|
2956
3040
|
while (current) {
|
|
2957
|
-
if (current.getKind() ===
|
|
3041
|
+
if (current.getKind() === SyntaxKind4.CallExpression) {
|
|
2958
3042
|
const text = current.getText();
|
|
2959
3043
|
if (text.startsWith("z.object(")) return true;
|
|
2960
3044
|
}
|
|
@@ -2966,7 +3050,7 @@ function checkAstPatterns(sourceFile) {
|
|
|
2966
3050
|
const results = [];
|
|
2967
3051
|
const filePath = sourceFile.getFilePath();
|
|
2968
3052
|
sourceFile.forEachDescendant((node) => {
|
|
2969
|
-
if (node.getKind() ===
|
|
3053
|
+
if (node.getKind() === SyntaxKind4.CallExpression) {
|
|
2970
3054
|
const text = node.getText();
|
|
2971
3055
|
if (text.startsWith("z.lazy(")) {
|
|
2972
3056
|
results.push({
|
|
@@ -2978,11 +3062,11 @@ function checkAstPatterns(sourceFile) {
|
|
|
2978
3062
|
});
|
|
2979
3063
|
}
|
|
2980
3064
|
if (text.startsWith("z.enum(")) {
|
|
2981
|
-
const callExpr = node.asKind(
|
|
3065
|
+
const callExpr = node.asKind(SyntaxKind4.CallExpression);
|
|
2982
3066
|
const args = callExpr?.getArguments();
|
|
2983
3067
|
if (args && args.length > 0) {
|
|
2984
3068
|
const firstArg = args[0];
|
|
2985
|
-
if (firstArg?.getKind() ===
|
|
3069
|
+
if (firstArg?.getKind() === SyntaxKind4.Identifier) {
|
|
2986
3070
|
results.push({
|
|
2987
3071
|
ruleId: "VAULT-AST-004",
|
|
2988
3072
|
severity: getRuleSeverity("VAULT-AST-004"),
|
|
@@ -2995,11 +3079,11 @@ function checkAstPatterns(sourceFile) {
|
|
|
2995
3079
|
}
|
|
2996
3080
|
}
|
|
2997
3081
|
if (text.startsWith("z.object(")) {
|
|
2998
|
-
const callExpr = node.asKind(
|
|
3082
|
+
const callExpr = node.asKind(SyntaxKind4.CallExpression);
|
|
2999
3083
|
const args = callExpr?.getArguments();
|
|
3000
3084
|
if (args && args.length > 0) {
|
|
3001
3085
|
const firstArg = args[0];
|
|
3002
|
-
if (firstArg?.getKind() ===
|
|
3086
|
+
if (firstArg?.getKind() === SyntaxKind4.Identifier) {
|
|
3003
3087
|
results.push({
|
|
3004
3088
|
ruleId: "VAULT-AST-006",
|
|
3005
3089
|
severity: getRuleSeverity("VAULT-AST-006"),
|
|
@@ -3011,11 +3095,11 @@ function checkAstPatterns(sourceFile) {
|
|
|
3011
3095
|
}
|
|
3012
3096
|
}
|
|
3013
3097
|
}
|
|
3014
|
-
if (node.getKind() ===
|
|
3098
|
+
if (node.getKind() === SyntaxKind4.SpreadAssignment) {
|
|
3015
3099
|
if (isInsideZodObject(node)) {
|
|
3016
|
-
const spreadExpr = node.asKind(
|
|
3100
|
+
const spreadExpr = node.asKind(SyntaxKind4.SpreadAssignment);
|
|
3017
3101
|
const expr = spreadExpr?.getExpression();
|
|
3018
|
-
if (expr?.getKind() ===
|
|
3102
|
+
if (expr?.getKind() === SyntaxKind4.ConditionalExpression || expr?.getKind() === SyntaxKind4.Identifier) {
|
|
3019
3103
|
results.push({
|
|
3020
3104
|
ruleId: "VAULT-AST-002",
|
|
3021
3105
|
severity: getRuleSeverity("VAULT-AST-002"),
|
|
@@ -3026,8 +3110,8 @@ function checkAstPatterns(sourceFile) {
|
|
|
3026
3110
|
}
|
|
3027
3111
|
}
|
|
3028
3112
|
}
|
|
3029
|
-
if (node.getKind() ===
|
|
3030
|
-
const funcNode = node.asKind(
|
|
3113
|
+
if (node.getKind() === SyntaxKind4.FunctionDeclaration || node.getKind() === SyntaxKind4.ArrowFunction) {
|
|
3114
|
+
const funcNode = node.asKind(SyntaxKind4.FunctionDeclaration) || node.asKind(SyntaxKind4.ArrowFunction);
|
|
3031
3115
|
if (funcNode) {
|
|
3032
3116
|
const body = funcNode.getBody();
|
|
3033
3117
|
if (body) {
|
|
@@ -3059,7 +3143,7 @@ var vaultChecker = {
|
|
|
3059
3143
|
}
|
|
3060
3144
|
const configResult = loadConfig(ctx.cwd);
|
|
3061
3145
|
const disabledRules = configResult.success ? getDisabledVaultRules(configResult.config) : [];
|
|
3062
|
-
const project = new
|
|
3146
|
+
const project = new Project4({
|
|
3063
3147
|
skipAddingFilesFromTsConfig: true,
|
|
3064
3148
|
skipFileDependencyResolution: true
|
|
3065
3149
|
});
|
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-4LVIIP2D.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.12.
|
|
3
|
+
"version": "1.12.2",
|
|
4
4
|
"description": "TypeScript dev kit for Claude Code agents: architecture linting, scaffolding, knowledge analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -47,8 +47,6 @@
|
|
|
47
47
|
"homepage": "https://github.com/hyphaene/hexa-ts-kit#readme",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
50
|
-
"@typescript-eslint/parser": "^8.50.1",
|
|
51
|
-
"@typescript-eslint/typescript-estree": "^8.50.1",
|
|
52
50
|
"commander": "^12.1.0",
|
|
53
51
|
"fast-glob": "^3.3.2",
|
|
54
52
|
"gray-matter": "^4.0.3",
|
|
@@ -60,7 +58,6 @@
|
|
|
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
|
},
|