depwire-cli 0.9.27 → 0.9.29
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-RGD3YJYQ.js → chunk-FUIZQCYB.js} +16 -10
- package/dist/{chunk-DA5LWNJ4.js → chunk-WUSXCZXA.js} +551 -104
- package/dist/index.js +13 -13
- package/dist/mcpb-entry.js +3 -3
- package/dist/sdk.d.ts +31 -2
- package/dist/sdk.js +3 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/utils/files.ts
|
|
2
|
-
import { readdirSync, statSync, existsSync, lstatSync } from "fs";
|
|
2
|
+
import { readdirSync, statSync, existsSync, lstatSync, realpathSync } from "fs";
|
|
3
3
|
import { join, relative } from "path";
|
|
4
4
|
import os from "os";
|
|
5
5
|
function scanDirectory(rootDir, baseDir = rootDir) {
|
|
@@ -50,6 +50,7 @@ function fileExists(filePath) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
function findProjectRoot(startDir = process.cwd()) {
|
|
53
|
+
startDir = realpathSync(startDir);
|
|
53
54
|
const projectMarkers = [
|
|
54
55
|
"package.json",
|
|
55
56
|
// Node.js
|
|
@@ -79,9 +80,6 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
79
80
|
let depth = 0;
|
|
80
81
|
const home = os.homedir();
|
|
81
82
|
while (currentDir !== rootDir && depth < maxDepth) {
|
|
82
|
-
if (currentDir === home || !currentDir.startsWith(home)) {
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
83
|
const dirName = currentDir.split("/").pop();
|
|
86
84
|
if (dirName && blocklist.includes(dirName)) {
|
|
87
85
|
console.warn(`\u26A0\uFE0F Skipping blocked directory: ${dirName}`);
|
|
@@ -2873,9 +2871,449 @@ async function parseProject(projectRoot, options) {
|
|
|
2873
2871
|
return parsedFiles;
|
|
2874
2872
|
}
|
|
2875
2873
|
|
|
2874
|
+
// src/cross-language/detectors/rest-api.ts
|
|
2875
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
2876
|
+
import { join as join9, resolve as resolve4 } from "path";
|
|
2877
|
+
function getLanguage(filePath) {
|
|
2878
|
+
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
|
|
2879
|
+
if (filePath.endsWith(".js") || filePath.endsWith(".jsx") || filePath.endsWith(".mjs") || filePath.endsWith(".cjs")) return "javascript";
|
|
2880
|
+
if (filePath.endsWith(".py")) return "python";
|
|
2881
|
+
if (filePath.endsWith(".go")) return "go";
|
|
2882
|
+
return "unknown";
|
|
2883
|
+
}
|
|
2884
|
+
function normalizePath(routePath) {
|
|
2885
|
+
return routePath.replace(/:[a-zA-Z_][a-zA-Z0-9_]*/g, "__PARAM__").replace(/\{[a-zA-Z_][a-zA-Z0-9_]*\}/g, "__PARAM__");
|
|
2886
|
+
}
|
|
2887
|
+
function stripTrailingSlash(p) {
|
|
2888
|
+
return p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
|
|
2889
|
+
}
|
|
2890
|
+
function extractHttpCalls(source, filePath) {
|
|
2891
|
+
const calls = [];
|
|
2892
|
+
const lines = source.split("\n");
|
|
2893
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2894
|
+
const line = lines[i];
|
|
2895
|
+
const fetchMatch = line.match(/fetch\s*\(\s*(['"`])([^'"`]+)\1/);
|
|
2896
|
+
if (fetchMatch) {
|
|
2897
|
+
const path6 = fetchMatch[2];
|
|
2898
|
+
if (isLocalApiPath(path6)) {
|
|
2899
|
+
const methodMatch = line.match(/method\s*:\s*['"](\w+)['"]/);
|
|
2900
|
+
const method = methodMatch ? methodMatch[1].toUpperCase() : "GET";
|
|
2901
|
+
calls.push({ method, path: cleanPath(path6), file: filePath, line: i + 1 });
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
if (!fetchMatch) {
|
|
2905
|
+
const fetchTemplateMatch = line.match(/fetch\s*\(\s*`([^`]+)`/);
|
|
2906
|
+
if (fetchTemplateMatch) {
|
|
2907
|
+
const path6 = fetchTemplateMatch[1];
|
|
2908
|
+
if (isLocalApiPath(path6)) {
|
|
2909
|
+
const methodMatch = line.match(/method\s*:\s*['"](\w+)['"]/);
|
|
2910
|
+
const method = methodMatch ? methodMatch[1].toUpperCase() : "GET";
|
|
2911
|
+
calls.push({ method, path: cleanPath(path6), file: filePath, line: i + 1 });
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
const axiosMatch = line.match(/axios\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"`])([^'"`]+)\2/i);
|
|
2916
|
+
if (axiosMatch) {
|
|
2917
|
+
const path6 = axiosMatch[3];
|
|
2918
|
+
if (isLocalApiPath(path6)) {
|
|
2919
|
+
calls.push({ method: axiosMatch[1].toUpperCase(), path: cleanPath(path6), file: filePath, line: i + 1 });
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
if (!axiosMatch) {
|
|
2923
|
+
const axiosTemplateMatch = line.match(/axios\s*\.\s*(get|post|put|delete|patch)\s*\(\s*`([^`]+)`/i);
|
|
2924
|
+
if (axiosTemplateMatch) {
|
|
2925
|
+
const path6 = axiosTemplateMatch[2];
|
|
2926
|
+
if (isLocalApiPath(path6)) {
|
|
2927
|
+
calls.push({ method: axiosTemplateMatch[1].toUpperCase(), path: cleanPath(path6), file: filePath, line: i + 1 });
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
const genericMatch = line.match(/\w+\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"`])([^'"`]+)\2/i);
|
|
2932
|
+
if (genericMatch && !line.match(/axios/) && !line.match(/app\s*\./) && !line.match(/router\s*\./) && !line.match(/r\s*\./)) {
|
|
2933
|
+
const path6 = genericMatch[3];
|
|
2934
|
+
if (isLocalApiPath(path6)) {
|
|
2935
|
+
calls.push({ method: genericMatch[1].toUpperCase(), path: cleanPath(path6), file: filePath, line: i + 1 });
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
return calls;
|
|
2940
|
+
}
|
|
2941
|
+
function isLocalApiPath(path6) {
|
|
2942
|
+
if (path6.startsWith("http://") || path6.startsWith("https://")) return false;
|
|
2943
|
+
return path6.startsWith("/") || path6.includes("/api/");
|
|
2944
|
+
}
|
|
2945
|
+
function cleanPath(path6) {
|
|
2946
|
+
let cleaned = path6.replace(/\$\{[^}]*\}/g, "");
|
|
2947
|
+
cleaned = stripTrailingSlash(cleaned);
|
|
2948
|
+
return cleaned;
|
|
2949
|
+
}
|
|
2950
|
+
function extractRouteDefinitions(source, filePath) {
|
|
2951
|
+
const routes = [];
|
|
2952
|
+
const lines = source.split("\n");
|
|
2953
|
+
const lang = getLanguage(filePath);
|
|
2954
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2955
|
+
const line = lines[i];
|
|
2956
|
+
if (lang === "typescript" || lang === "javascript") {
|
|
2957
|
+
const expressMatch = line.match(/(?:app|router)\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"`])([^'"`]+)\2/i);
|
|
2958
|
+
if (expressMatch) {
|
|
2959
|
+
const path6 = expressMatch[3];
|
|
2960
|
+
if (path6.startsWith("/")) {
|
|
2961
|
+
routes.push({
|
|
2962
|
+
method: expressMatch[1].toUpperCase(),
|
|
2963
|
+
path: path6,
|
|
2964
|
+
normalizedPath: normalizePath(path6),
|
|
2965
|
+
file: filePath,
|
|
2966
|
+
line: i + 1
|
|
2967
|
+
});
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
if (lang === "python") {
|
|
2972
|
+
const pythonMatch = line.match(/@(?:app|router)\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"])([^'"]+)\2/i);
|
|
2973
|
+
if (pythonMatch) {
|
|
2974
|
+
const path6 = pythonMatch[3];
|
|
2975
|
+
if (path6.startsWith("/")) {
|
|
2976
|
+
routes.push({
|
|
2977
|
+
method: pythonMatch[1].toUpperCase(),
|
|
2978
|
+
path: path6,
|
|
2979
|
+
normalizedPath: normalizePath(path6),
|
|
2980
|
+
file: filePath,
|
|
2981
|
+
line: i + 1
|
|
2982
|
+
});
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
const flaskMatch = line.match(/@(?:app|blueprint|router)\s*\.\s*route\s*\(\s*(['"])([^'"]+)\1/);
|
|
2986
|
+
if (flaskMatch) {
|
|
2987
|
+
const path6 = flaskMatch[2];
|
|
2988
|
+
if (path6.startsWith("/")) {
|
|
2989
|
+
const methodsMatch = line.match(/methods\s*=\s*\[([^\]]+)\]/);
|
|
2990
|
+
const methods = methodsMatch ? methodsMatch[1].match(/['"](\w+)['"]/g)?.map((m) => m.replace(/['"]/g, "").toUpperCase()) || ["GET"] : ["GET"];
|
|
2991
|
+
for (const method of methods) {
|
|
2992
|
+
routes.push({
|
|
2993
|
+
method,
|
|
2994
|
+
path: path6,
|
|
2995
|
+
normalizedPath: normalizePath(path6),
|
|
2996
|
+
file: filePath,
|
|
2997
|
+
line: i + 1
|
|
2998
|
+
});
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
if (lang === "go") {
|
|
3004
|
+
const goMatch = line.match(/(?:r|router|group)\s*\.\s*(GET|POST|PUT|DELETE|PATCH)\s*\(\s*"([^"]+)"/);
|
|
3005
|
+
if (goMatch) {
|
|
3006
|
+
const path6 = goMatch[2];
|
|
3007
|
+
if (path6.startsWith("/")) {
|
|
3008
|
+
routes.push({
|
|
3009
|
+
method: goMatch[1].toUpperCase(),
|
|
3010
|
+
path: path6,
|
|
3011
|
+
normalizedPath: normalizePath(path6),
|
|
3012
|
+
file: filePath,
|
|
3013
|
+
line: i + 1
|
|
3014
|
+
});
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
return routes;
|
|
3020
|
+
}
|
|
3021
|
+
function matchPaths(callPath, routeNormalized) {
|
|
3022
|
+
const normalizedCall = normalizePath(stripTrailingSlash(callPath));
|
|
3023
|
+
const normalizedRoute = stripTrailingSlash(routeNormalized);
|
|
3024
|
+
if (normalizedCall === normalizedRoute) return true;
|
|
3025
|
+
if (normalizedRoute.startsWith(normalizedCall) && normalizedRoute[normalizedCall.length] === "/") return true;
|
|
3026
|
+
const callParts = normalizedCall.split("/");
|
|
3027
|
+
const routeParts = normalizedRoute.split("/");
|
|
3028
|
+
if (callParts.length <= routeParts.length) {
|
|
3029
|
+
let match = true;
|
|
3030
|
+
for (let i = 0; i < callParts.length; i++) {
|
|
3031
|
+
if (routeParts[i] === "__PARAM__") continue;
|
|
3032
|
+
if (callParts[i] !== routeParts[i]) {
|
|
3033
|
+
match = false;
|
|
3034
|
+
break;
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
if (match) return true;
|
|
3038
|
+
}
|
|
3039
|
+
return false;
|
|
3040
|
+
}
|
|
3041
|
+
function getConfidence(callPath, callMethod, routePath, routeMethod) {
|
|
3042
|
+
const normalizedCall = normalizePath(stripTrailingSlash(callPath));
|
|
3043
|
+
const normalizedRoute = normalizePath(stripTrailingSlash(routePath));
|
|
3044
|
+
const exactPath = normalizedCall === normalizedRoute;
|
|
3045
|
+
const methodMatch = callMethod === routeMethod;
|
|
3046
|
+
if (exactPath && methodMatch) return "high";
|
|
3047
|
+
if (exactPath) return "medium";
|
|
3048
|
+
if (methodMatch) return "medium";
|
|
3049
|
+
return "low";
|
|
3050
|
+
}
|
|
3051
|
+
function detectRestApiEdges(files, projectRoot) {
|
|
3052
|
+
const edges = [];
|
|
3053
|
+
const allCalls = [];
|
|
3054
|
+
const allRoutes = [];
|
|
3055
|
+
for (const file of files) {
|
|
3056
|
+
const fullPath = join9(projectRoot, file.filePath);
|
|
3057
|
+
if (!resolve4(fullPath).startsWith(resolve4(projectRoot))) continue;
|
|
3058
|
+
let source;
|
|
3059
|
+
try {
|
|
3060
|
+
source = readFileSync6(fullPath, "utf-8");
|
|
3061
|
+
} catch {
|
|
3062
|
+
continue;
|
|
3063
|
+
}
|
|
3064
|
+
const lang = getLanguage(file.filePath);
|
|
3065
|
+
if (lang === "typescript" || lang === "javascript") {
|
|
3066
|
+
allCalls.push(...extractHttpCalls(source, file.filePath));
|
|
3067
|
+
}
|
|
3068
|
+
allRoutes.push(...extractRouteDefinitions(source, file.filePath));
|
|
3069
|
+
}
|
|
3070
|
+
for (const call of allCalls) {
|
|
3071
|
+
for (const route of allRoutes) {
|
|
3072
|
+
if (call.file === route.file) continue;
|
|
3073
|
+
if (matchPaths(call.path, route.normalizedPath)) {
|
|
3074
|
+
const confidence = getConfidence(call.path, call.method, route.path, route.method);
|
|
3075
|
+
edges.push({
|
|
3076
|
+
sourceFile: call.file,
|
|
3077
|
+
targetFile: route.file,
|
|
3078
|
+
edgeType: "rest-api",
|
|
3079
|
+
confidence,
|
|
3080
|
+
sourceLanguage: getLanguage(call.file),
|
|
3081
|
+
targetLanguage: getLanguage(route.file),
|
|
3082
|
+
sourceLine: call.line,
|
|
3083
|
+
targetLine: route.line,
|
|
3084
|
+
metadata: {
|
|
3085
|
+
httpMethod: call.method,
|
|
3086
|
+
path: call.path
|
|
3087
|
+
}
|
|
3088
|
+
});
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
return edges;
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
// src/cross-language/detectors/subprocess.ts
|
|
3096
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
3097
|
+
import { join as join10, resolve as resolve5, basename } from "path";
|
|
3098
|
+
var SCRIPT_EXTENSIONS = [".py", ".js", ".ts", ".go", ".rs"];
|
|
3099
|
+
function getLanguage2(filePath) {
|
|
3100
|
+
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
|
|
3101
|
+
if (filePath.endsWith(".js") || filePath.endsWith(".jsx") || filePath.endsWith(".mjs") || filePath.endsWith(".cjs")) return "javascript";
|
|
3102
|
+
if (filePath.endsWith(".py")) return "python";
|
|
3103
|
+
if (filePath.endsWith(".go")) return "go";
|
|
3104
|
+
if (filePath.endsWith(".rs")) return "rust";
|
|
3105
|
+
return "unknown";
|
|
3106
|
+
}
|
|
3107
|
+
function extractFilenameFromArgs(args) {
|
|
3108
|
+
const tokens = args.split(/[\s,'"[\]]+/).filter(Boolean);
|
|
3109
|
+
for (const token of tokens) {
|
|
3110
|
+
for (const ext of SCRIPT_EXTENSIONS) {
|
|
3111
|
+
if (token.endsWith(ext)) {
|
|
3112
|
+
return token;
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
return null;
|
|
3117
|
+
}
|
|
3118
|
+
function extractSubprocessCalls(source, filePath) {
|
|
3119
|
+
const calls = [];
|
|
3120
|
+
const lines = source.split("\n");
|
|
3121
|
+
const lang = getLanguage2(filePath);
|
|
3122
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3123
|
+
const line = lines[i];
|
|
3124
|
+
if (lang === "typescript" || lang === "javascript") {
|
|
3125
|
+
const execMatch = line.match(/(?:execSync|exec)\s*\(\s*(['"`])([^'"`]+)\1/);
|
|
3126
|
+
if (execMatch) {
|
|
3127
|
+
const command = execMatch[2];
|
|
3128
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3129
|
+
if (calledFile) {
|
|
3130
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
if (!execMatch) {
|
|
3134
|
+
const execTemplateMatch = line.match(/(?:execSync|exec)\s*\(\s*`([^`]+)`/);
|
|
3135
|
+
if (execTemplateMatch) {
|
|
3136
|
+
const command = execTemplateMatch[1].replace(/\$\{[^}]*\}/g, "");
|
|
3137
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3138
|
+
if (calledFile) {
|
|
3139
|
+
calls.push({ file: filePath, line: i + 1, command: execTemplateMatch[1], calledFile });
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
const spawnMatch = line.match(/(?:spawn|spawnSync)\s*\(\s*['"](\w+)['"]\s*,\s*\[([^\]]*)\]/);
|
|
3144
|
+
if (spawnMatch) {
|
|
3145
|
+
const command = `${spawnMatch[1]} ${spawnMatch[2]}`;
|
|
3146
|
+
const calledFile = extractFilenameFromArgs(spawnMatch[2]);
|
|
3147
|
+
if (calledFile) {
|
|
3148
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
if (lang === "python") {
|
|
3153
|
+
const subprocessMatch = line.match(/subprocess\s*\.\s*(?:run|call|Popen|check_output|check_call)\s*\(\s*\[([^\]]*)\]/);
|
|
3154
|
+
if (subprocessMatch) {
|
|
3155
|
+
const command = subprocessMatch[1];
|
|
3156
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3157
|
+
if (calledFile) {
|
|
3158
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
const osMatch = line.match(/os\s*\.\s*system\s*\(\s*['"]([^'"]+)['"]/);
|
|
3162
|
+
if (osMatch) {
|
|
3163
|
+
const command = osMatch[1];
|
|
3164
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3165
|
+
if (calledFile) {
|
|
3166
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
const subprocessStrMatch = line.match(/subprocess\s*\.\s*(?:run|call|Popen|check_output|check_call)\s*\(\s*['"]([^'"]+)['"]/);
|
|
3170
|
+
if (subprocessStrMatch) {
|
|
3171
|
+
const command = subprocessStrMatch[1];
|
|
3172
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3173
|
+
if (calledFile) {
|
|
3174
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
if (lang === "go") {
|
|
3179
|
+
const goMatch = line.match(/exec\s*\.\s*Command\s*\(\s*"([^"]+)"\s*,\s*"([^"]+)"/);
|
|
3180
|
+
if (goMatch) {
|
|
3181
|
+
const command = `${goMatch[1]} ${goMatch[2]}`;
|
|
3182
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3183
|
+
if (calledFile) {
|
|
3184
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
return calls;
|
|
3190
|
+
}
|
|
3191
|
+
function detectSubprocessEdges(files, projectRoot) {
|
|
3192
|
+
const edges = [];
|
|
3193
|
+
const knownFiles = new Set(files.map((f) => f.filePath));
|
|
3194
|
+
const basenameMap = /* @__PURE__ */ new Map();
|
|
3195
|
+
for (const f of files) {
|
|
3196
|
+
const base = basename(f.filePath);
|
|
3197
|
+
if (!basenameMap.has(base)) basenameMap.set(base, []);
|
|
3198
|
+
basenameMap.get(base).push(f.filePath);
|
|
3199
|
+
}
|
|
3200
|
+
for (const file of files) {
|
|
3201
|
+
const fullPath = join10(projectRoot, file.filePath);
|
|
3202
|
+
if (!resolve5(fullPath).startsWith(resolve5(projectRoot))) continue;
|
|
3203
|
+
let source;
|
|
3204
|
+
try {
|
|
3205
|
+
source = readFileSync7(fullPath, "utf-8");
|
|
3206
|
+
} catch {
|
|
3207
|
+
continue;
|
|
3208
|
+
}
|
|
3209
|
+
const calls = extractSubprocessCalls(source, file.filePath);
|
|
3210
|
+
for (const call of calls) {
|
|
3211
|
+
let targetFile = null;
|
|
3212
|
+
let confidence = "high";
|
|
3213
|
+
if (knownFiles.has(call.calledFile)) {
|
|
3214
|
+
targetFile = call.calledFile;
|
|
3215
|
+
confidence = "high";
|
|
3216
|
+
} else {
|
|
3217
|
+
const base = basename(call.calledFile);
|
|
3218
|
+
const candidates = basenameMap.get(base);
|
|
3219
|
+
if (candidates && candidates.length > 0) {
|
|
3220
|
+
const exactCandidate = candidates.find((c) => c.endsWith(call.calledFile));
|
|
3221
|
+
if (exactCandidate) {
|
|
3222
|
+
targetFile = exactCandidate;
|
|
3223
|
+
confidence = "high";
|
|
3224
|
+
} else {
|
|
3225
|
+
targetFile = candidates[0];
|
|
3226
|
+
confidence = "medium";
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
if (!targetFile) continue;
|
|
3231
|
+
if (targetFile === call.file) continue;
|
|
3232
|
+
edges.push({
|
|
3233
|
+
sourceFile: call.file,
|
|
3234
|
+
targetFile,
|
|
3235
|
+
edgeType: "subprocess",
|
|
3236
|
+
confidence,
|
|
3237
|
+
sourceLanguage: getLanguage2(call.file),
|
|
3238
|
+
targetLanguage: getLanguage2(targetFile),
|
|
3239
|
+
sourceLine: call.line,
|
|
3240
|
+
metadata: {
|
|
3241
|
+
command: call.command,
|
|
3242
|
+
calledFile: call.calledFile
|
|
3243
|
+
}
|
|
3244
|
+
});
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
return edges;
|
|
3248
|
+
}
|
|
3249
|
+
|
|
3250
|
+
// src/cross-language/index.ts
|
|
3251
|
+
function detectCrossLanguageEdges(files, projectRoot, graph) {
|
|
3252
|
+
const startTime = Date.now();
|
|
3253
|
+
const restApiEdges = detectRestApiEdges(files, projectRoot);
|
|
3254
|
+
const subprocessEdges = detectSubprocessEdges(files, projectRoot);
|
|
3255
|
+
const allEdges = [...restApiEdges, ...subprocessEdges];
|
|
3256
|
+
for (const edge of allEdges) {
|
|
3257
|
+
const sourceNodeId = `${edge.sourceFile}::__file__`;
|
|
3258
|
+
const targetNodeId = `${edge.targetFile}::__file__`;
|
|
3259
|
+
if (!graph.hasNode(sourceNodeId)) {
|
|
3260
|
+
let hasSourceFile = false;
|
|
3261
|
+
graph.forEachNode((_nodeId, attrs) => {
|
|
3262
|
+
if (attrs.filePath === edge.sourceFile) hasSourceFile = true;
|
|
3263
|
+
});
|
|
3264
|
+
if (!hasSourceFile) continue;
|
|
3265
|
+
graph.addNode(sourceNodeId, {
|
|
3266
|
+
name: "__file__",
|
|
3267
|
+
kind: "import",
|
|
3268
|
+
filePath: edge.sourceFile,
|
|
3269
|
+
startLine: 1,
|
|
3270
|
+
endLine: 1,
|
|
3271
|
+
exported: false
|
|
3272
|
+
});
|
|
3273
|
+
}
|
|
3274
|
+
if (!graph.hasNode(targetNodeId)) {
|
|
3275
|
+
let hasTargetFile = false;
|
|
3276
|
+
graph.forEachNode((_nodeId, attrs) => {
|
|
3277
|
+
if (attrs.filePath === edge.targetFile) hasTargetFile = true;
|
|
3278
|
+
});
|
|
3279
|
+
if (!hasTargetFile) continue;
|
|
3280
|
+
graph.addNode(targetNodeId, {
|
|
3281
|
+
name: "__file__",
|
|
3282
|
+
kind: "import",
|
|
3283
|
+
filePath: edge.targetFile,
|
|
3284
|
+
startLine: 1,
|
|
3285
|
+
endLine: 1,
|
|
3286
|
+
exported: false
|
|
3287
|
+
});
|
|
3288
|
+
}
|
|
3289
|
+
graph.mergeEdge(sourceNodeId, targetNodeId, {
|
|
3290
|
+
kind: edge.edgeType,
|
|
3291
|
+
filePath: edge.sourceFile,
|
|
3292
|
+
line: edge.sourceLine || 1,
|
|
3293
|
+
crossLanguage: true,
|
|
3294
|
+
confidence: edge.confidence,
|
|
3295
|
+
edgeType: edge.edgeType,
|
|
3296
|
+
httpMethod: edge.metadata.httpMethod,
|
|
3297
|
+
path: edge.metadata.path,
|
|
3298
|
+
command: edge.metadata.command,
|
|
3299
|
+
calledFile: edge.metadata.calledFile
|
|
3300
|
+
});
|
|
3301
|
+
}
|
|
3302
|
+
const detectionTimeMs = Date.now() - startTime;
|
|
3303
|
+
return {
|
|
3304
|
+
edges: allEdges,
|
|
3305
|
+
stats: {
|
|
3306
|
+
restApiEdges: restApiEdges.length,
|
|
3307
|
+
subprocessEdges: subprocessEdges.length,
|
|
3308
|
+
filesAnalyzed: files.length,
|
|
3309
|
+
detectionTimeMs
|
|
3310
|
+
}
|
|
3311
|
+
};
|
|
3312
|
+
}
|
|
3313
|
+
|
|
2876
3314
|
// src/graph/index.ts
|
|
2877
3315
|
import { DirectedGraph } from "graphology";
|
|
2878
|
-
function buildGraph(parsedFiles) {
|
|
3316
|
+
function buildGraph(parsedFiles, projectRoot) {
|
|
2879
3317
|
const graph = new DirectedGraph();
|
|
2880
3318
|
for (const file of parsedFiles) {
|
|
2881
3319
|
for (const symbol of file.symbols) {
|
|
@@ -2932,6 +3370,12 @@ function buildGraph(parsedFiles) {
|
|
|
2932
3370
|
}
|
|
2933
3371
|
}
|
|
2934
3372
|
}
|
|
3373
|
+
if (projectRoot) {
|
|
3374
|
+
const result = detectCrossLanguageEdges(parsedFiles, projectRoot, graph);
|
|
3375
|
+
if (result.stats.restApiEdges > 0 || result.stats.subprocessEdges > 0) {
|
|
3376
|
+
console.error(`Cross-language edges: ${result.stats.restApiEdges} rest-api, ${result.stats.subprocessEdges} subprocess detected`);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
2935
3379
|
return graph;
|
|
2936
3380
|
}
|
|
2937
3381
|
|
|
@@ -3063,7 +3507,9 @@ function getCrossFileEdges(graph) {
|
|
|
3063
3507
|
target,
|
|
3064
3508
|
sourceFile: sourceAttrs.filePath,
|
|
3065
3509
|
targetFile: targetAttrs.filePath,
|
|
3066
|
-
kind: attrs.kind
|
|
3510
|
+
kind: attrs.kind,
|
|
3511
|
+
crossLanguage: attrs.crossLanguage || false,
|
|
3512
|
+
edgeType: attrs.edgeType
|
|
3067
3513
|
});
|
|
3068
3514
|
}
|
|
3069
3515
|
});
|
|
@@ -3511,8 +3957,8 @@ function calculateDepthScore(graph) {
|
|
|
3511
3957
|
}
|
|
3512
3958
|
|
|
3513
3959
|
// src/health/index.ts
|
|
3514
|
-
import { readFileSync as
|
|
3515
|
-
import { dirname as dirname8, resolve as
|
|
3960
|
+
import { readFileSync as readFileSync8, writeFileSync, existsSync as existsSync8, mkdirSync } from "fs";
|
|
3961
|
+
import { dirname as dirname8, resolve as resolve6 } from "path";
|
|
3516
3962
|
function calculateHealthScore(graph, projectRoot) {
|
|
3517
3963
|
const coupling = calculateCouplingScore(graph);
|
|
3518
3964
|
const cohesion = calculateCohesionScore(graph);
|
|
@@ -3611,8 +4057,8 @@ function getHealthTrend(projectRoot, currentScore) {
|
|
|
3611
4057
|
}
|
|
3612
4058
|
}
|
|
3613
4059
|
function saveHealthHistory(projectRoot, report) {
|
|
3614
|
-
const resolvedRoot =
|
|
3615
|
-
const historyFile =
|
|
4060
|
+
const resolvedRoot = resolve6(projectRoot);
|
|
4061
|
+
const historyFile = resolve6(resolvedRoot, ".depwire", "health-history.json");
|
|
3616
4062
|
if (!historyFile.startsWith(resolvedRoot)) {
|
|
3617
4063
|
return;
|
|
3618
4064
|
}
|
|
@@ -3630,7 +4076,7 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
3630
4076
|
if (existsSync8(historyFile)) {
|
|
3631
4077
|
try {
|
|
3632
4078
|
if (!historyFile.startsWith(resolvedRoot)) return;
|
|
3633
|
-
const content =
|
|
4079
|
+
const content = readFileSync8(historyFile, "utf-8");
|
|
3634
4080
|
history = JSON.parse(content);
|
|
3635
4081
|
} catch {
|
|
3636
4082
|
}
|
|
@@ -3644,14 +4090,14 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
3644
4090
|
writeFileSync(historyFile, JSON.stringify(history, null, 2), "utf-8");
|
|
3645
4091
|
}
|
|
3646
4092
|
function loadHealthHistory(projectRoot) {
|
|
3647
|
-
const resolvedRoot =
|
|
3648
|
-
const historyFile =
|
|
4093
|
+
const resolvedRoot = resolve6(projectRoot);
|
|
4094
|
+
const historyFile = resolve6(resolvedRoot, ".depwire", "health-history.json");
|
|
3649
4095
|
if (!historyFile.startsWith(resolvedRoot) || !existsSync8(historyFile)) {
|
|
3650
4096
|
return [];
|
|
3651
4097
|
}
|
|
3652
4098
|
try {
|
|
3653
4099
|
if (!historyFile.startsWith(resolvedRoot)) return [];
|
|
3654
|
-
const content =
|
|
4100
|
+
const content = readFileSync8(historyFile, "utf-8");
|
|
3655
4101
|
return JSON.parse(content);
|
|
3656
4102
|
} catch {
|
|
3657
4103
|
return [];
|
|
@@ -3660,7 +4106,7 @@ function loadHealthHistory(projectRoot) {
|
|
|
3660
4106
|
|
|
3661
4107
|
// src/dead-code/detector.ts
|
|
3662
4108
|
import path2 from "path";
|
|
3663
|
-
import { readFileSync as
|
|
4109
|
+
import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
3664
4110
|
function findDeadSymbols(graph, projectRoot, includeTests = false, debug = false) {
|
|
3665
4111
|
const deadSymbols = [];
|
|
3666
4112
|
const context = { graph, projectRoot };
|
|
@@ -3795,7 +4241,7 @@ function getPackageEntryPoints(projectRoot) {
|
|
|
3795
4241
|
return entryPoints;
|
|
3796
4242
|
}
|
|
3797
4243
|
try {
|
|
3798
|
-
const packageJson = JSON.parse(
|
|
4244
|
+
const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
|
|
3799
4245
|
if (packageJson.main) {
|
|
3800
4246
|
entryPoints.add(path2.resolve(projectRoot, packageJson.main));
|
|
3801
4247
|
}
|
|
@@ -3936,8 +4382,8 @@ function generateReason(symbol, confidence) {
|
|
|
3936
4382
|
return "Potentially unused";
|
|
3937
4383
|
}
|
|
3938
4384
|
function isBarrelFile(filePath) {
|
|
3939
|
-
const
|
|
3940
|
-
return
|
|
4385
|
+
const basename5 = path3.basename(filePath);
|
|
4386
|
+
return basename5 === "index.ts" || basename5 === "index.js";
|
|
3941
4387
|
}
|
|
3942
4388
|
function isTestFile2(filePath) {
|
|
3943
4389
|
return filePath.includes("__tests__/") || filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("/test/") || filePath.includes("/tests/");
|
|
@@ -4117,7 +4563,7 @@ function filterByConfidence(symbols, minConfidence) {
|
|
|
4117
4563
|
|
|
4118
4564
|
// src/docs/generator.ts
|
|
4119
4565
|
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync12 } from "fs";
|
|
4120
|
-
import { join as
|
|
4566
|
+
import { join as join14 } from "path";
|
|
4121
4567
|
|
|
4122
4568
|
// src/docs/architecture.ts
|
|
4123
4569
|
import { dirname as dirname9 } from "path";
|
|
@@ -4519,7 +4965,7 @@ function detectCycles(graph) {
|
|
|
4519
4965
|
}
|
|
4520
4966
|
|
|
4521
4967
|
// src/docs/conventions.ts
|
|
4522
|
-
import { basename, extname as extname4 } from "path";
|
|
4968
|
+
import { basename as basename2, extname as extname4 } from "path";
|
|
4523
4969
|
function generateConventions(graph, projectRoot, version) {
|
|
4524
4970
|
let output = "";
|
|
4525
4971
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -4557,7 +5003,7 @@ function generateFileOrganization(graph) {
|
|
|
4557
5003
|
graph.forEachNode((node, attrs) => {
|
|
4558
5004
|
if (!files.has(attrs.filePath)) {
|
|
4559
5005
|
files.add(attrs.filePath);
|
|
4560
|
-
const fileName =
|
|
5006
|
+
const fileName = basename2(attrs.filePath);
|
|
4561
5007
|
if (fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx") {
|
|
4562
5008
|
barrelFileCount++;
|
|
4563
5009
|
}
|
|
@@ -4616,7 +5062,7 @@ function generateNamingPatterns(graph) {
|
|
|
4616
5062
|
graph.forEachNode((node, attrs) => {
|
|
4617
5063
|
if (!files.has(attrs.filePath)) {
|
|
4618
5064
|
files.add(attrs.filePath);
|
|
4619
|
-
const fileName =
|
|
5065
|
+
const fileName = basename2(attrs.filePath, extname4(attrs.filePath));
|
|
4620
5066
|
if (isCamelCase(fileName)) patterns.files.camelCase++;
|
|
4621
5067
|
else if (isPascalCase(fileName)) patterns.files.PascalCase++;
|
|
4622
5068
|
else if (isKebabCase(fileName)) patterns.files.kebabCase++;
|
|
@@ -5681,7 +6127,7 @@ function generateDepwireUsage(projectRoot) {
|
|
|
5681
6127
|
}
|
|
5682
6128
|
|
|
5683
6129
|
// src/docs/files.ts
|
|
5684
|
-
import { dirname as dirname11, basename as
|
|
6130
|
+
import { dirname as dirname11, basename as basename3 } from "path";
|
|
5685
6131
|
function generateFiles(graph, projectRoot, version) {
|
|
5686
6132
|
let output = "";
|
|
5687
6133
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -5800,7 +6246,7 @@ function generateDirectoryBreakdown(graph) {
|
|
|
5800
6246
|
dirStats.symbolCount += file.symbolCount;
|
|
5801
6247
|
if (file.totalConnections > dirStats.maxConnections) {
|
|
5802
6248
|
dirStats.maxConnections = file.totalConnections;
|
|
5803
|
-
dirStats.mostConnectedFile =
|
|
6249
|
+
dirStats.mostConnectedFile = basename3(file.filePath);
|
|
5804
6250
|
}
|
|
5805
6251
|
}
|
|
5806
6252
|
if (dirMap.size === 0) {
|
|
@@ -6428,7 +6874,7 @@ function generateRecommendations(graph) {
|
|
|
6428
6874
|
}
|
|
6429
6875
|
|
|
6430
6876
|
// src/docs/tests.ts
|
|
6431
|
-
import { basename as
|
|
6877
|
+
import { basename as basename4, dirname as dirname12 } from "path";
|
|
6432
6878
|
function generateTests(graph, projectRoot, version) {
|
|
6433
6879
|
let output = "";
|
|
6434
6880
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -6456,7 +6902,7 @@ function getFileCount8(graph) {
|
|
|
6456
6902
|
return files.size;
|
|
6457
6903
|
}
|
|
6458
6904
|
function isTestFile3(filePath) {
|
|
6459
|
-
const fileName =
|
|
6905
|
+
const fileName = basename4(filePath).toLowerCase();
|
|
6460
6906
|
const dirPath = dirname12(filePath).toLowerCase();
|
|
6461
6907
|
if (dirPath.includes("test") || dirPath.includes("spec") || dirPath.includes("__tests__")) {
|
|
6462
6908
|
return true;
|
|
@@ -6515,7 +6961,7 @@ function generateTestFileInventory(graph) {
|
|
|
6515
6961
|
return output;
|
|
6516
6962
|
}
|
|
6517
6963
|
function matchTestToSource(testFile) {
|
|
6518
|
-
const testFileName =
|
|
6964
|
+
const testFileName = basename4(testFile);
|
|
6519
6965
|
const testDir = dirname12(testFile);
|
|
6520
6966
|
let sourceFileName = testFileName.replace(/\.test\./g, ".").replace(/\.spec\./g, ".").replace(/_test\./g, ".").replace(/_spec\./g, ".");
|
|
6521
6967
|
const possiblePaths = [];
|
|
@@ -6677,7 +7123,7 @@ function generateTestCoverageMap(graph) {
|
|
|
6677
7123
|
const rows = mappings.slice(0, 30).map((m) => [
|
|
6678
7124
|
`\`${m.sourceFile}\``,
|
|
6679
7125
|
m.hasTest ? "\u2705" : "\u274C",
|
|
6680
|
-
m.testFile ? `\`${
|
|
7126
|
+
m.testFile ? `\`${basename4(m.testFile)}\`` : "-",
|
|
6681
7127
|
formatNumber(m.symbolCount)
|
|
6682
7128
|
]);
|
|
6683
7129
|
let output = table(headers, rows);
|
|
@@ -7453,8 +7899,8 @@ function getTopLevelDir2(filePath) {
|
|
|
7453
7899
|
}
|
|
7454
7900
|
|
|
7455
7901
|
// src/docs/status.ts
|
|
7456
|
-
import { readFileSync as
|
|
7457
|
-
import { resolve as
|
|
7902
|
+
import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
|
|
7903
|
+
import { resolve as resolve7 } from "path";
|
|
7458
7904
|
function generateStatus(graph, projectRoot, version) {
|
|
7459
7905
|
let output = "";
|
|
7460
7906
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -7487,8 +7933,8 @@ function getFileCount11(graph) {
|
|
|
7487
7933
|
}
|
|
7488
7934
|
function extractComments(projectRoot, filePath) {
|
|
7489
7935
|
const comments = [];
|
|
7490
|
-
const resolvedRoot =
|
|
7491
|
-
const fullPath =
|
|
7936
|
+
const resolvedRoot = resolve7(projectRoot);
|
|
7937
|
+
const fullPath = resolve7(resolvedRoot, filePath);
|
|
7492
7938
|
if (!fullPath.startsWith(resolvedRoot)) {
|
|
7493
7939
|
return comments;
|
|
7494
7940
|
}
|
|
@@ -7496,7 +7942,7 @@ function extractComments(projectRoot, filePath) {
|
|
|
7496
7942
|
return comments;
|
|
7497
7943
|
}
|
|
7498
7944
|
try {
|
|
7499
|
-
const content =
|
|
7945
|
+
const content = readFileSync10(fullPath, "utf-8");
|
|
7500
7946
|
const lines = content.split("\n");
|
|
7501
7947
|
const patterns = [
|
|
7502
7948
|
{ type: "TODO", regex: /(?:\/\/|#|\/\*)\s*TODO:?\s*(.+)/i },
|
|
@@ -8075,16 +8521,16 @@ function generateConfidenceSection(title, description, symbols, projectRoot) {
|
|
|
8075
8521
|
}
|
|
8076
8522
|
|
|
8077
8523
|
// src/docs/metadata.ts
|
|
8078
|
-
import { existsSync as existsSync11, readFileSync as
|
|
8079
|
-
import { resolve as
|
|
8524
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync2 } from "fs";
|
|
8525
|
+
import { resolve as resolve8 } from "path";
|
|
8080
8526
|
function loadMetadata(outputDir) {
|
|
8081
|
-
const resolvedDir =
|
|
8082
|
-
const metadataPath =
|
|
8527
|
+
const resolvedDir = resolve8(outputDir);
|
|
8528
|
+
const metadataPath = resolve8(resolvedDir, "metadata.json");
|
|
8083
8529
|
if (!metadataPath.startsWith(resolvedDir) || !existsSync11(metadataPath)) {
|
|
8084
8530
|
return null;
|
|
8085
8531
|
}
|
|
8086
8532
|
try {
|
|
8087
|
-
const content =
|
|
8533
|
+
const content = readFileSync11(metadataPath, "utf-8");
|
|
8088
8534
|
return JSON.parse(content);
|
|
8089
8535
|
} catch (err) {
|
|
8090
8536
|
console.error("Failed to load metadata:", err);
|
|
@@ -8092,8 +8538,8 @@ function loadMetadata(outputDir) {
|
|
|
8092
8538
|
}
|
|
8093
8539
|
}
|
|
8094
8540
|
function saveMetadata(outputDir, metadata) {
|
|
8095
|
-
const resolvedDir =
|
|
8096
|
-
const metadataPath =
|
|
8541
|
+
const resolvedDir = resolve8(outputDir);
|
|
8542
|
+
const metadataPath = resolve8(resolvedDir, "metadata.json");
|
|
8097
8543
|
if (!metadataPath.startsWith(resolvedDir)) {
|
|
8098
8544
|
throw new Error(`Path traversal attempt blocked: ${metadataPath}`);
|
|
8099
8545
|
}
|
|
@@ -8178,7 +8624,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8178
8624
|
try {
|
|
8179
8625
|
if (options.verbose) console.log("Generating ARCHITECTURE.md...");
|
|
8180
8626
|
const content = generateArchitecture(graph, projectRoot, version, parseTime);
|
|
8181
|
-
const filePath =
|
|
8627
|
+
const filePath = join14(options.outputDir, "ARCHITECTURE.md");
|
|
8182
8628
|
writeFileSync3(filePath, content, "utf-8");
|
|
8183
8629
|
generated.push("ARCHITECTURE.md");
|
|
8184
8630
|
} catch (err) {
|
|
@@ -8189,7 +8635,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8189
8635
|
try {
|
|
8190
8636
|
if (options.verbose) console.log("Generating CONVENTIONS.md...");
|
|
8191
8637
|
const content = generateConventions(graph, projectRoot, version);
|
|
8192
|
-
const filePath =
|
|
8638
|
+
const filePath = join14(options.outputDir, "CONVENTIONS.md");
|
|
8193
8639
|
writeFileSync3(filePath, content, "utf-8");
|
|
8194
8640
|
generated.push("CONVENTIONS.md");
|
|
8195
8641
|
} catch (err) {
|
|
@@ -8200,7 +8646,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8200
8646
|
try {
|
|
8201
8647
|
if (options.verbose) console.log("Generating DEPENDENCIES.md...");
|
|
8202
8648
|
const content = generateDependencies(graph, projectRoot, version);
|
|
8203
|
-
const filePath =
|
|
8649
|
+
const filePath = join14(options.outputDir, "DEPENDENCIES.md");
|
|
8204
8650
|
writeFileSync3(filePath, content, "utf-8");
|
|
8205
8651
|
generated.push("DEPENDENCIES.md");
|
|
8206
8652
|
} catch (err) {
|
|
@@ -8211,7 +8657,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8211
8657
|
try {
|
|
8212
8658
|
if (options.verbose) console.log("Generating ONBOARDING.md...");
|
|
8213
8659
|
const content = generateOnboarding(graph, projectRoot, version);
|
|
8214
|
-
const filePath =
|
|
8660
|
+
const filePath = join14(options.outputDir, "ONBOARDING.md");
|
|
8215
8661
|
writeFileSync3(filePath, content, "utf-8");
|
|
8216
8662
|
generated.push("ONBOARDING.md");
|
|
8217
8663
|
} catch (err) {
|
|
@@ -8222,7 +8668,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8222
8668
|
try {
|
|
8223
8669
|
if (options.verbose) console.log("Generating FILES.md...");
|
|
8224
8670
|
const content = generateFiles(graph, projectRoot, version);
|
|
8225
|
-
const filePath =
|
|
8671
|
+
const filePath = join14(options.outputDir, "FILES.md");
|
|
8226
8672
|
writeFileSync3(filePath, content, "utf-8");
|
|
8227
8673
|
generated.push("FILES.md");
|
|
8228
8674
|
} catch (err) {
|
|
@@ -8233,7 +8679,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8233
8679
|
try {
|
|
8234
8680
|
if (options.verbose) console.log("Generating API_SURFACE.md...");
|
|
8235
8681
|
const content = generateApiSurface(graph, projectRoot, version);
|
|
8236
|
-
const filePath =
|
|
8682
|
+
const filePath = join14(options.outputDir, "API_SURFACE.md");
|
|
8237
8683
|
writeFileSync3(filePath, content, "utf-8");
|
|
8238
8684
|
generated.push("API_SURFACE.md");
|
|
8239
8685
|
} catch (err) {
|
|
@@ -8244,7 +8690,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8244
8690
|
try {
|
|
8245
8691
|
if (options.verbose) console.log("Generating ERRORS.md...");
|
|
8246
8692
|
const content = generateErrors(graph, projectRoot, version);
|
|
8247
|
-
const filePath =
|
|
8693
|
+
const filePath = join14(options.outputDir, "ERRORS.md");
|
|
8248
8694
|
writeFileSync3(filePath, content, "utf-8");
|
|
8249
8695
|
generated.push("ERRORS.md");
|
|
8250
8696
|
} catch (err) {
|
|
@@ -8255,7 +8701,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8255
8701
|
try {
|
|
8256
8702
|
if (options.verbose) console.log("Generating TESTS.md...");
|
|
8257
8703
|
const content = generateTests(graph, projectRoot, version);
|
|
8258
|
-
const filePath =
|
|
8704
|
+
const filePath = join14(options.outputDir, "TESTS.md");
|
|
8259
8705
|
writeFileSync3(filePath, content, "utf-8");
|
|
8260
8706
|
generated.push("TESTS.md");
|
|
8261
8707
|
} catch (err) {
|
|
@@ -8266,7 +8712,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8266
8712
|
try {
|
|
8267
8713
|
if (options.verbose) console.log("Generating HISTORY.md...");
|
|
8268
8714
|
const content = generateHistory(graph, projectRoot, version);
|
|
8269
|
-
const filePath =
|
|
8715
|
+
const filePath = join14(options.outputDir, "HISTORY.md");
|
|
8270
8716
|
writeFileSync3(filePath, content, "utf-8");
|
|
8271
8717
|
generated.push("HISTORY.md");
|
|
8272
8718
|
} catch (err) {
|
|
@@ -8277,7 +8723,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8277
8723
|
try {
|
|
8278
8724
|
if (options.verbose) console.log("Generating CURRENT.md...");
|
|
8279
8725
|
const content = generateCurrent(graph, projectRoot, version);
|
|
8280
|
-
const filePath =
|
|
8726
|
+
const filePath = join14(options.outputDir, "CURRENT.md");
|
|
8281
8727
|
writeFileSync3(filePath, content, "utf-8");
|
|
8282
8728
|
generated.push("CURRENT.md");
|
|
8283
8729
|
} catch (err) {
|
|
@@ -8288,7 +8734,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8288
8734
|
try {
|
|
8289
8735
|
if (options.verbose) console.log("Generating STATUS.md...");
|
|
8290
8736
|
const content = generateStatus(graph, projectRoot, version);
|
|
8291
|
-
const filePath =
|
|
8737
|
+
const filePath = join14(options.outputDir, "STATUS.md");
|
|
8292
8738
|
writeFileSync3(filePath, content, "utf-8");
|
|
8293
8739
|
generated.push("STATUS.md");
|
|
8294
8740
|
} catch (err) {
|
|
@@ -8299,7 +8745,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8299
8745
|
try {
|
|
8300
8746
|
if (options.verbose) console.log("Generating HEALTH.md...");
|
|
8301
8747
|
const content = generateHealth(graph, projectRoot, version);
|
|
8302
|
-
const filePath =
|
|
8748
|
+
const filePath = join14(options.outputDir, "HEALTH.md");
|
|
8303
8749
|
writeFileSync3(filePath, content, "utf-8");
|
|
8304
8750
|
generated.push("HEALTH.md");
|
|
8305
8751
|
} catch (err) {
|
|
@@ -8310,7 +8756,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8310
8756
|
try {
|
|
8311
8757
|
if (options.verbose) console.log("Generating DEAD_CODE.md...");
|
|
8312
8758
|
const content = generateDeadCode(graph, projectRoot, version);
|
|
8313
|
-
const filePath =
|
|
8759
|
+
const filePath = join14(options.outputDir, "DEAD_CODE.md");
|
|
8314
8760
|
writeFileSync3(filePath, content, "utf-8");
|
|
8315
8761
|
generated.push("DEAD_CODE.md");
|
|
8316
8762
|
} catch (err) {
|
|
@@ -8354,13 +8800,13 @@ function getFileCount13(graph) {
|
|
|
8354
8800
|
}
|
|
8355
8801
|
|
|
8356
8802
|
// src/simulation/engine.ts
|
|
8357
|
-
import { dirname as dirname15, join as
|
|
8358
|
-
function
|
|
8803
|
+
import { dirname as dirname15, join as join15 } from "path";
|
|
8804
|
+
function normalizePath2(p) {
|
|
8359
8805
|
return p.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
8360
8806
|
}
|
|
8361
8807
|
function fileMatch(nodeFilePath, target) {
|
|
8362
|
-
const a =
|
|
8363
|
-
const b =
|
|
8808
|
+
const a = normalizePath2(nodeFilePath);
|
|
8809
|
+
const b = normalizePath2(target);
|
|
8364
8810
|
return a === b || a.endsWith("/" + b) || b.endsWith("/" + a);
|
|
8365
8811
|
}
|
|
8366
8812
|
var SimulationEngine = class {
|
|
@@ -8425,8 +8871,8 @@ var SimulationEngine = class {
|
|
|
8425
8871
|
}
|
|
8426
8872
|
// ── Action implementations ─────────────────────────────────────
|
|
8427
8873
|
applyMove(clone, target, destination, brokenImports) {
|
|
8428
|
-
const normalizedTarget =
|
|
8429
|
-
const normalizedDest =
|
|
8874
|
+
const normalizedTarget = normalizePath2(target);
|
|
8875
|
+
const normalizedDest = normalizePath2(destination);
|
|
8430
8876
|
const nodesToMove = clone.filterNodes(
|
|
8431
8877
|
(_node, attrs) => fileMatch(attrs.filePath, target)
|
|
8432
8878
|
);
|
|
@@ -8485,11 +8931,11 @@ var SimulationEngine = class {
|
|
|
8485
8931
|
}
|
|
8486
8932
|
}
|
|
8487
8933
|
applyRename(clone, target, newName, brokenImports) {
|
|
8488
|
-
const destination =
|
|
8934
|
+
const destination = join15(dirname15(target), newName);
|
|
8489
8935
|
this.applyMove(clone, target, destination, brokenImports);
|
|
8490
8936
|
}
|
|
8491
8937
|
applySplit(clone, target, newFile, symbols, brokenImports) {
|
|
8492
|
-
const normalizedNewFile =
|
|
8938
|
+
const normalizedNewFile = normalizePath2(newFile);
|
|
8493
8939
|
const nodesToSplit = clone.filterNodes((_node, attrs) => {
|
|
8494
8940
|
return fileMatch(attrs.filePath, target) && symbols.includes(attrs.name);
|
|
8495
8941
|
});
|
|
@@ -8525,7 +8971,7 @@ var SimulationEngine = class {
|
|
|
8525
8971
|
}
|
|
8526
8972
|
}
|
|
8527
8973
|
applyMerge(clone, target, source, brokenImports) {
|
|
8528
|
-
const normalizedTarget =
|
|
8974
|
+
const normalizedTarget = normalizePath2(target);
|
|
8529
8975
|
const sourceNodes = clone.filterNodes(
|
|
8530
8976
|
(_node, attrs) => fileMatch(attrs.filePath, source)
|
|
8531
8977
|
);
|
|
@@ -8686,12 +9132,12 @@ var SimulationEngine = class {
|
|
|
8686
9132
|
|
|
8687
9133
|
// src/security/scanner.ts
|
|
8688
9134
|
import { existsSync as existsSync14 } from "fs";
|
|
8689
|
-
import { join as
|
|
9135
|
+
import { join as join25 } from "path";
|
|
8690
9136
|
|
|
8691
9137
|
// src/security/checks/dependencies.ts
|
|
8692
9138
|
import { execSync as execSync2 } from "child_process";
|
|
8693
|
-
import { existsSync as existsSync13, readFileSync as
|
|
8694
|
-
import { join as
|
|
9139
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12, readdirSync as readdirSync5 } from "fs";
|
|
9140
|
+
import { join as join16 } from "path";
|
|
8695
9141
|
function cvssToSeverity(score) {
|
|
8696
9142
|
if (score >= 9) return "critical";
|
|
8697
9143
|
if (score >= 7) return "high";
|
|
@@ -8701,18 +9147,18 @@ function cvssToSeverity(score) {
|
|
|
8701
9147
|
async function checkDependencies(_files, projectRoot) {
|
|
8702
9148
|
const findings = [];
|
|
8703
9149
|
try {
|
|
8704
|
-
if (existsSync13(
|
|
9150
|
+
if (existsSync13(join16(projectRoot, "package.json"))) {
|
|
8705
9151
|
findings.push(...checkNpmAudit(projectRoot));
|
|
8706
9152
|
findings.push(...checkPackageJsonPatterns(projectRoot));
|
|
8707
9153
|
findings.push(...checkPostinstallScripts(projectRoot));
|
|
8708
9154
|
}
|
|
8709
|
-
if (existsSync13(
|
|
9155
|
+
if (existsSync13(join16(projectRoot, "requirements.txt")) || existsSync13(join16(projectRoot, "pyproject.toml"))) {
|
|
8710
9156
|
findings.push(...checkPipAudit(projectRoot));
|
|
8711
9157
|
}
|
|
8712
|
-
if (existsSync13(
|
|
9158
|
+
if (existsSync13(join16(projectRoot, "Cargo.toml"))) {
|
|
8713
9159
|
findings.push(...checkCargoAudit(projectRoot));
|
|
8714
9160
|
}
|
|
8715
|
-
if (existsSync13(
|
|
9161
|
+
if (existsSync13(join16(projectRoot, "go.mod"))) {
|
|
8716
9162
|
findings.push(...checkGoVerify(projectRoot));
|
|
8717
9163
|
}
|
|
8718
9164
|
} catch (err) {
|
|
@@ -8801,8 +9247,8 @@ function checkNpmAudit(projectRoot) {
|
|
|
8801
9247
|
function checkPackageJsonPatterns(projectRoot) {
|
|
8802
9248
|
const findings = [];
|
|
8803
9249
|
try {
|
|
8804
|
-
const pkgPath =
|
|
8805
|
-
const pkg = JSON.parse(
|
|
9250
|
+
const pkgPath = join16(projectRoot, "package.json");
|
|
9251
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
8806
9252
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
8807
9253
|
for (const [name, version] of Object.entries(allDeps)) {
|
|
8808
9254
|
if (version.startsWith("^") || version.startsWith("~")) {
|
|
@@ -8824,15 +9270,15 @@ function checkPackageJsonPatterns(projectRoot) {
|
|
|
8824
9270
|
}
|
|
8825
9271
|
function checkPostinstallScripts(projectRoot) {
|
|
8826
9272
|
const findings = [];
|
|
8827
|
-
const nodeModules =
|
|
9273
|
+
const nodeModules = join16(projectRoot, "node_modules");
|
|
8828
9274
|
if (!existsSync13(nodeModules)) return findings;
|
|
8829
9275
|
try {
|
|
8830
9276
|
const topLevelDeps = readdirSync5(nodeModules).filter((d) => !d.startsWith("."));
|
|
8831
9277
|
for (const dep of topLevelDeps) {
|
|
8832
|
-
const depPkgPath =
|
|
9278
|
+
const depPkgPath = join16(nodeModules, dep, "package.json");
|
|
8833
9279
|
if (!existsSync13(depPkgPath)) continue;
|
|
8834
9280
|
try {
|
|
8835
|
-
const depPkg = JSON.parse(
|
|
9281
|
+
const depPkg = JSON.parse(readFileSync12(depPkgPath, "utf-8"));
|
|
8836
9282
|
const scripts = depPkg.scripts || {};
|
|
8837
9283
|
if (scripts.postinstall || scripts.preinstall || scripts.install) {
|
|
8838
9284
|
const scriptName = scripts.postinstall ? "postinstall" : scripts.preinstall ? "preinstall" : "install";
|
|
@@ -8870,7 +9316,7 @@ function checkPipAudit(projectRoot) {
|
|
|
8870
9316
|
id: "",
|
|
8871
9317
|
severity: cvssToSeverity(vuln.cvss?.score || 5),
|
|
8872
9318
|
vulnerabilityClass: "dependency-cve",
|
|
8873
|
-
file: existsSync13(
|
|
9319
|
+
file: existsSync13(join16(projectRoot, "requirements.txt")) ? "requirements.txt" : "pyproject.toml",
|
|
8874
9320
|
title: `Vulnerable Python dependency: ${vuln.name}`,
|
|
8875
9321
|
description: `${vuln.name}@${vuln.version} \u2014 ${vuln.id}: ${vuln.description || "Known vulnerability"}`,
|
|
8876
9322
|
attackScenario: `An attacker could exploit the vulnerability in ${vuln.name}.`,
|
|
@@ -8967,8 +9413,8 @@ function checkGoVerify(projectRoot) {
|
|
|
8967
9413
|
}
|
|
8968
9414
|
|
|
8969
9415
|
// src/security/checks/injection.ts
|
|
8970
|
-
import { readFileSync as
|
|
8971
|
-
import { join as
|
|
9416
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
9417
|
+
import { join as join17 } from "path";
|
|
8972
9418
|
var SKIP_DIRS = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
8973
9419
|
var TEST_PATTERNS = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__"];
|
|
8974
9420
|
var USER_INPUT_NAMES = /(?:input|user|name|path|query|branch|hash|cmd|command|req\.|params|body|args|url|dir|file|subdirectory)/i;
|
|
@@ -9069,7 +9515,7 @@ async function checkInjection(files, projectRoot) {
|
|
|
9069
9515
|
if (shouldSkip(file.filePath) || isTestFile4(file.filePath)) continue;
|
|
9070
9516
|
let content;
|
|
9071
9517
|
try {
|
|
9072
|
-
content =
|
|
9518
|
+
content = readFileSync13(join17(projectRoot, file.filePath), "utf-8");
|
|
9073
9519
|
} catch {
|
|
9074
9520
|
continue;
|
|
9075
9521
|
}
|
|
@@ -9107,8 +9553,8 @@ async function checkInjection(files, projectRoot) {
|
|
|
9107
9553
|
}
|
|
9108
9554
|
|
|
9109
9555
|
// src/security/checks/secrets.ts
|
|
9110
|
-
import { readFileSync as
|
|
9111
|
-
import { join as
|
|
9556
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
9557
|
+
import { join as join18 } from "path";
|
|
9112
9558
|
var SKIP_DIRS2 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9113
9559
|
var TEST_PATTERNS2 = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__", ".example", ".sample"];
|
|
9114
9560
|
var SECRET_PATTERNS = [
|
|
@@ -9141,7 +9587,7 @@ async function checkSecrets(files, projectRoot) {
|
|
|
9141
9587
|
if (shouldSkip2(file.filePath) || isTestFile5(file.filePath)) continue;
|
|
9142
9588
|
let content;
|
|
9143
9589
|
try {
|
|
9144
|
-
content =
|
|
9590
|
+
content = readFileSync14(join18(projectRoot, file.filePath), "utf-8");
|
|
9145
9591
|
} catch {
|
|
9146
9592
|
continue;
|
|
9147
9593
|
}
|
|
@@ -9174,8 +9620,8 @@ async function checkSecrets(files, projectRoot) {
|
|
|
9174
9620
|
}
|
|
9175
9621
|
|
|
9176
9622
|
// src/security/checks/path-traversal.ts
|
|
9177
|
-
import { readFileSync as
|
|
9178
|
-
import { join as
|
|
9623
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
9624
|
+
import { join as join19 } from "path";
|
|
9179
9625
|
var SKIP_DIRS3 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9180
9626
|
var USER_INPUT_VARS = /(?:req\.|params|query|body|input|path|dir|subdirectory|file|userInput|fileName|filePath)/i;
|
|
9181
9627
|
var PATTERNS2 = [
|
|
@@ -9222,7 +9668,7 @@ async function checkPathTraversal(files, projectRoot) {
|
|
|
9222
9668
|
if (shouldSkip3(file.filePath)) continue;
|
|
9223
9669
|
let content;
|
|
9224
9670
|
try {
|
|
9225
|
-
content =
|
|
9671
|
+
content = readFileSync15(join19(projectRoot, file.filePath), "utf-8");
|
|
9226
9672
|
} catch {
|
|
9227
9673
|
continue;
|
|
9228
9674
|
}
|
|
@@ -9264,8 +9710,8 @@ async function checkPathTraversal(files, projectRoot) {
|
|
|
9264
9710
|
}
|
|
9265
9711
|
|
|
9266
9712
|
// src/security/checks/auth.ts
|
|
9267
|
-
import { readFileSync as
|
|
9268
|
-
import { join as
|
|
9713
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
9714
|
+
import { join as join20 } from "path";
|
|
9269
9715
|
var SKIP_DIRS4 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9270
9716
|
function shouldSkip4(filePath) {
|
|
9271
9717
|
return SKIP_DIRS4.some((d) => filePath.includes(d));
|
|
@@ -9281,7 +9727,7 @@ async function checkAuth(files, projectRoot) {
|
|
|
9281
9727
|
if (shouldSkip4(file.filePath)) continue;
|
|
9282
9728
|
let content;
|
|
9283
9729
|
try {
|
|
9284
|
-
content =
|
|
9730
|
+
content = readFileSync16(join20(projectRoot, file.filePath), "utf-8");
|
|
9285
9731
|
} catch {
|
|
9286
9732
|
continue;
|
|
9287
9733
|
}
|
|
@@ -9372,8 +9818,8 @@ async function checkAuth(files, projectRoot) {
|
|
|
9372
9818
|
}
|
|
9373
9819
|
|
|
9374
9820
|
// src/security/checks/input-validation.ts
|
|
9375
|
-
import { readFileSync as
|
|
9376
|
-
import { join as
|
|
9821
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
9822
|
+
import { join as join21 } from "path";
|
|
9377
9823
|
var SKIP_DIRS5 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9378
9824
|
function shouldSkip5(filePath) {
|
|
9379
9825
|
return SKIP_DIRS5.some((d) => filePath.includes(d));
|
|
@@ -9385,7 +9831,7 @@ async function checkInputValidation(files, projectRoot) {
|
|
|
9385
9831
|
if (shouldSkip5(file.filePath)) continue;
|
|
9386
9832
|
let content;
|
|
9387
9833
|
try {
|
|
9388
|
-
content =
|
|
9834
|
+
content = readFileSync17(join21(projectRoot, file.filePath), "utf-8");
|
|
9389
9835
|
} catch {
|
|
9390
9836
|
continue;
|
|
9391
9837
|
}
|
|
@@ -9462,8 +9908,8 @@ async function checkInputValidation(files, projectRoot) {
|
|
|
9462
9908
|
}
|
|
9463
9909
|
|
|
9464
9910
|
// src/security/checks/information-disclosure.ts
|
|
9465
|
-
import { readFileSync as
|
|
9466
|
-
import { join as
|
|
9911
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
9912
|
+
import { join as join22 } from "path";
|
|
9467
9913
|
var SKIP_DIRS6 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9468
9914
|
function shouldSkip6(filePath) {
|
|
9469
9915
|
return SKIP_DIRS6.some((d) => filePath.includes(d));
|
|
@@ -9475,7 +9921,7 @@ async function checkInformationDisclosure(files, projectRoot) {
|
|
|
9475
9921
|
if (shouldSkip6(file.filePath)) continue;
|
|
9476
9922
|
let content;
|
|
9477
9923
|
try {
|
|
9478
|
-
content =
|
|
9924
|
+
content = readFileSync18(join22(projectRoot, file.filePath), "utf-8");
|
|
9479
9925
|
} catch {
|
|
9480
9926
|
continue;
|
|
9481
9927
|
}
|
|
@@ -9545,8 +9991,8 @@ async function checkInformationDisclosure(files, projectRoot) {
|
|
|
9545
9991
|
}
|
|
9546
9992
|
|
|
9547
9993
|
// src/security/checks/cryptography.ts
|
|
9548
|
-
import { readFileSync as
|
|
9549
|
-
import { join as
|
|
9994
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
9995
|
+
import { join as join23 } from "path";
|
|
9550
9996
|
var SKIP_DIRS7 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9551
9997
|
function shouldSkip7(filePath) {
|
|
9552
9998
|
return SKIP_DIRS7.some((d) => filePath.includes(d));
|
|
@@ -9562,7 +10008,7 @@ async function checkCryptography(files, projectRoot) {
|
|
|
9562
10008
|
if (shouldSkip7(file.filePath)) continue;
|
|
9563
10009
|
let content;
|
|
9564
10010
|
try {
|
|
9565
|
-
content =
|
|
10011
|
+
content = readFileSync19(join23(projectRoot, file.filePath), "utf-8");
|
|
9566
10012
|
} catch {
|
|
9567
10013
|
continue;
|
|
9568
10014
|
}
|
|
@@ -9644,8 +10090,8 @@ async function checkCryptography(files, projectRoot) {
|
|
|
9644
10090
|
}
|
|
9645
10091
|
|
|
9646
10092
|
// src/security/checks/frontend.ts
|
|
9647
|
-
import { readFileSync as
|
|
9648
|
-
import { join as
|
|
10093
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
10094
|
+
import { join as join24 } from "path";
|
|
9649
10095
|
var SKIP_DIRS8 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9650
10096
|
function shouldSkip8(filePath) {
|
|
9651
10097
|
return SKIP_DIRS8.some((d) => filePath.includes(d));
|
|
@@ -9661,7 +10107,7 @@ async function checkFrontend(files, projectRoot) {
|
|
|
9661
10107
|
if (!isFrontendFile(file.filePath)) continue;
|
|
9662
10108
|
let content;
|
|
9663
10109
|
try {
|
|
9664
|
-
content =
|
|
10110
|
+
content = readFileSync20(join24(projectRoot, file.filePath), "utf-8");
|
|
9665
10111
|
} catch {
|
|
9666
10112
|
continue;
|
|
9667
10113
|
}
|
|
@@ -10121,11 +10567,11 @@ async function scanSecurity(projectRoot, graph, options = {}) {
|
|
|
10121
10567
|
};
|
|
10122
10568
|
}
|
|
10123
10569
|
function detectPackageManager(projectRoot) {
|
|
10124
|
-
if (existsSync14(
|
|
10125
|
-
if (existsSync14(
|
|
10126
|
-
if (existsSync14(
|
|
10127
|
-
if (existsSync14(
|
|
10128
|
-
if (existsSync14(
|
|
10570
|
+
if (existsSync14(join25(projectRoot, "package.json"))) return "npm";
|
|
10571
|
+
if (existsSync14(join25(projectRoot, "requirements.txt"))) return "pip";
|
|
10572
|
+
if (existsSync14(join25(projectRoot, "pyproject.toml"))) return "pip";
|
|
10573
|
+
if (existsSync14(join25(projectRoot, "Cargo.toml"))) return "cargo";
|
|
10574
|
+
if (existsSync14(join25(projectRoot, "go.mod"))) return "go";
|
|
10129
10575
|
return "unknown";
|
|
10130
10576
|
}
|
|
10131
10577
|
|
|
@@ -10133,6 +10579,7 @@ export {
|
|
|
10133
10579
|
findProjectRoot,
|
|
10134
10580
|
parseTypeScriptFile,
|
|
10135
10581
|
parseProject,
|
|
10582
|
+
detectCrossLanguageEdges,
|
|
10136
10583
|
buildGraph,
|
|
10137
10584
|
findSymbols,
|
|
10138
10585
|
getDependencies,
|