depwire-cli 0.9.30 → 1.0.1

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.
@@ -32,7 +32,9 @@ function scanDirectory(rootDir, baseDir = rootDir) {
32
32
  const isGo = entry.endsWith(".go") && !entry.endsWith("_test.go");
33
33
  const isRust = entry.endsWith(".rs");
34
34
  const isC = entry.endsWith(".c") || entry.endsWith(".h");
35
- if (isTypeScript || isJavaScript || isPython || isGo || isRust || isC) {
35
+ const isCSharp = entry.endsWith(".cs") || entry.endsWith(".csx") || entry.endsWith(".csproj");
36
+ const isJava = entry.endsWith(".java") || entry === "pom.xml" || entry === "build.gradle" || entry === "build.gradle.kts";
37
+ if (isTypeScript || isJavaScript || isPython || isGo || isRust || isC || isCSharp || isJava) {
36
38
  files.push(relative(rootDir, fullPath));
37
39
  }
38
40
  }
@@ -70,6 +72,10 @@ function findProjectRoot(startDir = process.cwd()) {
70
72
  // C/C++ (cmake-based)
71
73
  "configure.ac",
72
74
  // C/C++ (autotools)
75
+ "pom.xml",
76
+ // Java (Maven)
77
+ "build.gradle",
78
+ // Java (Gradle)
73
79
  ".git"
74
80
  // Any git repo
75
81
  ];
@@ -103,11 +109,11 @@ function findProjectRoot(startDir = process.cwd()) {
103
109
  }
104
110
 
105
111
  // src/parser/index.ts
106
- import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
107
- import { join as join8, resolve as resolve3 } from "path";
112
+ import { readFileSync as readFileSync7, statSync as statSync4 } from "fs";
113
+ import { join as join10, resolve as resolve5 } from "path";
108
114
 
109
115
  // src/parser/detect.ts
110
- import { extname as extname3 } from "path";
116
+ import { extname as extname5, basename as basename3 } from "path";
111
117
 
112
118
  // src/parser/wasm-init.ts
113
119
  import { Parser, Language } from "web-tree-sitter";
@@ -134,7 +140,9 @@ async function initParser() {
134
140
  "python": "tree-sitter-python.wasm",
135
141
  "go": "tree-sitter-go.wasm",
136
142
  "rust": "tree-sitter-rust.wasm",
137
- "c": "tree-sitter-c.wasm"
143
+ "c": "tree-sitter-c.wasm",
144
+ "c_sharp": "tree-sitter-c_sharp.wasm",
145
+ "java": "tree-sitter-java.wasm"
138
146
  };
139
147
  for (const [name, file] of Object.entries(grammarFiles)) {
140
148
  const wasmPath = path.join(grammarsDir, file);
@@ -2782,6 +2790,1312 @@ var cParser = {
2782
2790
  parseFile: parseCFile
2783
2791
  };
2784
2792
 
2793
+ // src/parser/csharp.ts
2794
+ import { dirname as dirname7, join as join8, resolve as resolve3, basename } from "path";
2795
+ import { existsSync as existsSync8, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
2796
+ function parseCSharpFile(filePath, sourceCode, projectRoot) {
2797
+ if (filePath.endsWith(".csproj")) {
2798
+ return parseCsprojFile(filePath, sourceCode, projectRoot);
2799
+ }
2800
+ const parser = getParser("c_sharp");
2801
+ const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
2802
+ const context = {
2803
+ filePath,
2804
+ projectRoot,
2805
+ sourceCode,
2806
+ symbols: [],
2807
+ edges: [],
2808
+ currentScope: [],
2809
+ currentClass: null,
2810
+ currentNamespace: null,
2811
+ imports: /* @__PURE__ */ new Map(),
2812
+ isCsproj: false
2813
+ };
2814
+ walkNode7(tree.rootNode, context);
2815
+ return {
2816
+ filePath,
2817
+ symbols: context.symbols,
2818
+ edges: context.edges
2819
+ };
2820
+ }
2821
+ function walkNode7(node, context) {
2822
+ processNode7(node, context);
2823
+ for (let i = 0; i < node.childCount; i++) {
2824
+ const child = node.child(i);
2825
+ if (child) {
2826
+ walkNode7(child, context);
2827
+ }
2828
+ }
2829
+ }
2830
+ function processNode7(node, context) {
2831
+ switch (node.type) {
2832
+ case "namespace_declaration":
2833
+ processNamespaceDeclaration(node, context);
2834
+ break;
2835
+ case "file_scoped_namespace_declaration":
2836
+ processFileScopedNamespace(node, context);
2837
+ break;
2838
+ case "class_declaration":
2839
+ processClassDeclaration3(node, context);
2840
+ break;
2841
+ case "interface_declaration":
2842
+ processInterfaceDeclaration2(node, context);
2843
+ break;
2844
+ case "struct_declaration":
2845
+ processStructDeclaration(node, context);
2846
+ break;
2847
+ case "enum_declaration":
2848
+ processEnumDeclaration2(node, context);
2849
+ break;
2850
+ case "record_declaration":
2851
+ processRecordDeclaration(node, context);
2852
+ break;
2853
+ case "delegate_declaration":
2854
+ processDelegateDeclaration(node, context);
2855
+ break;
2856
+ case "method_declaration":
2857
+ processMethodDeclaration2(node, context);
2858
+ break;
2859
+ case "constructor_declaration":
2860
+ processConstructorDeclaration(node, context);
2861
+ break;
2862
+ case "property_declaration":
2863
+ processPropertyDeclaration(node, context);
2864
+ break;
2865
+ case "event_field_declaration":
2866
+ processEventFieldDeclaration(node, context);
2867
+ break;
2868
+ case "indexer_declaration":
2869
+ processIndexerDeclaration(node, context);
2870
+ break;
2871
+ case "using_directive":
2872
+ processUsingDirective(node, context);
2873
+ break;
2874
+ case "global_statement":
2875
+ processGlobalStatement(node, context);
2876
+ break;
2877
+ case "invocation_expression":
2878
+ processCallExpression7(node, context);
2879
+ break;
2880
+ }
2881
+ }
2882
+ function processNamespaceDeclaration(node, context) {
2883
+ const nameNode = node.childForFieldName("name");
2884
+ if (!nameNode) return;
2885
+ const name = nodeText6(nameNode, context);
2886
+ const symbolId = `${context.filePath}::${name}`;
2887
+ context.symbols.push({
2888
+ id: symbolId,
2889
+ name,
2890
+ kind: "module",
2891
+ filePath: context.filePath,
2892
+ startLine: node.startPosition.row + 1,
2893
+ endLine: node.endPosition.row + 1,
2894
+ exported: true
2895
+ });
2896
+ const oldNamespace = context.currentNamespace;
2897
+ context.currentNamespace = name;
2898
+ context.currentScope.push(name);
2899
+ const body = findChildByType7(node, "declaration_list");
2900
+ if (body) {
2901
+ walkNode7(body, context);
2902
+ }
2903
+ context.currentScope.pop();
2904
+ context.currentNamespace = oldNamespace;
2905
+ }
2906
+ function processFileScopedNamespace(node, context) {
2907
+ const nameNode = node.childForFieldName("name");
2908
+ if (!nameNode) return;
2909
+ const name = nodeText6(nameNode, context);
2910
+ const symbolId = `${context.filePath}::${name}`;
2911
+ context.symbols.push({
2912
+ id: symbolId,
2913
+ name,
2914
+ kind: "module",
2915
+ filePath: context.filePath,
2916
+ startLine: node.startPosition.row + 1,
2917
+ endLine: node.endPosition.row + 1,
2918
+ exported: true
2919
+ });
2920
+ context.currentNamespace = name;
2921
+ }
2922
+ function processClassDeclaration3(node, context) {
2923
+ processTypeDeclaration2(node, context, "class");
2924
+ }
2925
+ function processInterfaceDeclaration2(node, context) {
2926
+ processTypeDeclaration2(node, context, "interface");
2927
+ }
2928
+ function processStructDeclaration(node, context) {
2929
+ processTypeDeclaration2(node, context, "class");
2930
+ }
2931
+ function processRecordDeclaration(node, context) {
2932
+ processTypeDeclaration2(node, context, "class");
2933
+ }
2934
+ function processTypeDeclaration2(node, context, kind) {
2935
+ const nameNode = node.childForFieldName("name");
2936
+ if (!nameNode) return;
2937
+ let name = nodeText6(nameNode, context);
2938
+ const angleBracketIdx = name.indexOf("<");
2939
+ if (angleBracketIdx > 0) {
2940
+ name = name.substring(0, angleBracketIdx);
2941
+ }
2942
+ const exported = hasModifier(node, context, "public") || hasModifier(node, context, "internal");
2943
+ const scope = context.currentClass || void 0;
2944
+ const symbolId = `${context.filePath}::${name}`;
2945
+ context.symbols.push({
2946
+ id: symbolId,
2947
+ name,
2948
+ kind,
2949
+ filePath: context.filePath,
2950
+ startLine: node.startPosition.row + 1,
2951
+ endLine: node.endPosition.row + 1,
2952
+ exported,
2953
+ scope
2954
+ });
2955
+ const baseList = findChildByType7(node, "base_list");
2956
+ if (baseList) {
2957
+ for (let i = 0; i < baseList.childCount; i++) {
2958
+ const child = baseList.child(i);
2959
+ if (!child) continue;
2960
+ if (child.type === "simple_base_type" || child.type === "identifier" || child.type === "generic_name" || child.type === "qualified_name") {
2961
+ let baseName = extractBaseTypeName(child, context);
2962
+ if (baseName) {
2963
+ const baseId = resolveSymbol6(baseName, context);
2964
+ if (baseId) {
2965
+ const edgeKind = baseName.startsWith("I") && baseName.length > 1 && baseName[1] === baseName[1].toUpperCase() ? "implements" : "inherits";
2966
+ context.edges.push({
2967
+ source: symbolId,
2968
+ target: baseId,
2969
+ kind: edgeKind,
2970
+ filePath: context.filePath,
2971
+ line: child.startPosition.row + 1
2972
+ });
2973
+ }
2974
+ }
2975
+ }
2976
+ }
2977
+ }
2978
+ const oldClass = context.currentClass;
2979
+ context.currentClass = name;
2980
+ context.currentScope.push(name);
2981
+ const body = findChildByType7(node, "declaration_list");
2982
+ if (body) {
2983
+ walkNode7(body, context);
2984
+ }
2985
+ context.currentScope.pop();
2986
+ context.currentClass = oldClass;
2987
+ }
2988
+ function processEnumDeclaration2(node, context) {
2989
+ const nameNode = node.childForFieldName("name");
2990
+ if (!nameNode) return;
2991
+ const name = nodeText6(nameNode, context);
2992
+ const exported = hasModifier(node, context, "public") || hasModifier(node, context, "internal");
2993
+ const symbolId = `${context.filePath}::${name}`;
2994
+ context.symbols.push({
2995
+ id: symbolId,
2996
+ name,
2997
+ kind: "enum",
2998
+ filePath: context.filePath,
2999
+ startLine: node.startPosition.row + 1,
3000
+ endLine: node.endPosition.row + 1,
3001
+ exported
3002
+ });
3003
+ const body = findChildByType7(node, "enum_member_declaration_list");
3004
+ if (body) {
3005
+ const members = findChildrenByType2(body, "enum_member_declaration");
3006
+ for (const member of members) {
3007
+ const memberNameNode = member.childForFieldName("name");
3008
+ if (!memberNameNode) continue;
3009
+ const memberName = nodeText6(memberNameNode, context);
3010
+ const memberId = `${context.filePath}::${name}.${memberName}`;
3011
+ context.symbols.push({
3012
+ id: memberId,
3013
+ name: memberName,
3014
+ kind: "constant",
3015
+ filePath: context.filePath,
3016
+ startLine: member.startPosition.row + 1,
3017
+ endLine: member.endPosition.row + 1,
3018
+ exported,
3019
+ scope: name
3020
+ });
3021
+ }
3022
+ }
3023
+ }
3024
+ function processDelegateDeclaration(node, context) {
3025
+ const nameNode = node.childForFieldName("name");
3026
+ if (!nameNode) return;
3027
+ let name = nodeText6(nameNode, context);
3028
+ const angleBracketIdx = name.indexOf("<");
3029
+ if (angleBracketIdx > 0) name = name.substring(0, angleBracketIdx);
3030
+ const exported = hasModifier(node, context, "public") || hasModifier(node, context, "internal");
3031
+ const symbolId = `${context.filePath}::${name}`;
3032
+ context.symbols.push({
3033
+ id: symbolId,
3034
+ name,
3035
+ kind: "type_alias",
3036
+ filePath: context.filePath,
3037
+ startLine: node.startPosition.row + 1,
3038
+ endLine: node.endPosition.row + 1,
3039
+ exported
3040
+ });
3041
+ }
3042
+ function processMethodDeclaration2(node, context) {
3043
+ const nameNode = node.childForFieldName("name");
3044
+ if (!nameNode) return;
3045
+ const name = nodeText6(nameNode, context);
3046
+ const exported = hasModifier(node, context, "public");
3047
+ const scope = context.currentClass || void 0;
3048
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3049
+ context.symbols.push({
3050
+ id: symbolId,
3051
+ name,
3052
+ kind: context.currentClass ? "method" : "function",
3053
+ filePath: context.filePath,
3054
+ startLine: node.startPosition.row + 1,
3055
+ endLine: node.endPosition.row + 1,
3056
+ exported,
3057
+ scope
3058
+ });
3059
+ const scopeName = scope ? `${scope}.${name}` : name;
3060
+ context.currentScope.push(scopeName);
3061
+ const body = node.childForFieldName("body");
3062
+ if (body) {
3063
+ walkNode7(body, context);
3064
+ }
3065
+ context.currentScope.pop();
3066
+ }
3067
+ function processConstructorDeclaration(node, context) {
3068
+ const nameNode = node.childForFieldName("name");
3069
+ if (!nameNode) return;
3070
+ const name = nodeText6(nameNode, context);
3071
+ const exported = hasModifier(node, context, "public");
3072
+ const scope = context.currentClass || void 0;
3073
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3074
+ context.symbols.push({
3075
+ id: symbolId,
3076
+ name,
3077
+ kind: "method",
3078
+ filePath: context.filePath,
3079
+ startLine: node.startPosition.row + 1,
3080
+ endLine: node.endPosition.row + 1,
3081
+ exported,
3082
+ scope
3083
+ });
3084
+ const scopeName = scope ? `${scope}.${name}` : name;
3085
+ context.currentScope.push(scopeName);
3086
+ const body = node.childForFieldName("body");
3087
+ if (body) {
3088
+ walkNode7(body, context);
3089
+ }
3090
+ context.currentScope.pop();
3091
+ }
3092
+ function processPropertyDeclaration(node, context) {
3093
+ const nameNode = node.childForFieldName("name");
3094
+ if (!nameNode) return;
3095
+ const name = nodeText6(nameNode, context);
3096
+ const exported = hasModifier(node, context, "public");
3097
+ const scope = context.currentClass || void 0;
3098
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3099
+ context.symbols.push({
3100
+ id: symbolId,
3101
+ name,
3102
+ kind: "property",
3103
+ filePath: context.filePath,
3104
+ startLine: node.startPosition.row + 1,
3105
+ endLine: node.endPosition.row + 1,
3106
+ exported,
3107
+ scope
3108
+ });
3109
+ }
3110
+ function processEventFieldDeclaration(node, context) {
3111
+ const varDecl = findChildByType7(node, "variable_declaration");
3112
+ if (!varDecl) return;
3113
+ const declarator = findChildByType7(varDecl, "variable_declarator");
3114
+ if (!declarator) return;
3115
+ const nameNode = declarator.childForFieldName("name") || findChildByType7(declarator, "identifier");
3116
+ if (!nameNode) return;
3117
+ const name = nodeText6(nameNode, context);
3118
+ const exported = hasModifier(node, context, "public");
3119
+ const scope = context.currentClass || void 0;
3120
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3121
+ context.symbols.push({
3122
+ id: symbolId,
3123
+ name,
3124
+ kind: "property",
3125
+ filePath: context.filePath,
3126
+ startLine: node.startPosition.row + 1,
3127
+ endLine: node.endPosition.row + 1,
3128
+ exported,
3129
+ scope
3130
+ });
3131
+ }
3132
+ function processIndexerDeclaration(node, context) {
3133
+ const exported = hasModifier(node, context, "public");
3134
+ const scope = context.currentClass || void 0;
3135
+ const name = "this[]";
3136
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3137
+ context.symbols.push({
3138
+ id: symbolId,
3139
+ name,
3140
+ kind: "property",
3141
+ filePath: context.filePath,
3142
+ startLine: node.startPosition.row + 1,
3143
+ endLine: node.endPosition.row + 1,
3144
+ exported,
3145
+ scope
3146
+ });
3147
+ }
3148
+ function processUsingDirective(node, context) {
3149
+ const nameNode = node.childForFieldName("name");
3150
+ if (!nameNode) return;
3151
+ const name = nodeText6(nameNode, context);
3152
+ const resolvedPath = resolveCSharpNamespace(name, context.filePath, context.projectRoot);
3153
+ if (resolvedPath) {
3154
+ const sourceId = `${context.filePath}::__file__`;
3155
+ const targetId = `${resolvedPath}::__file__`;
3156
+ context.edges.push({
3157
+ source: sourceId,
3158
+ target: targetId,
3159
+ kind: "imports",
3160
+ filePath: context.filePath,
3161
+ line: node.startPosition.row + 1
3162
+ });
3163
+ }
3164
+ }
3165
+ function processGlobalStatement(node, context) {
3166
+ const symbolId = `${context.filePath}::__toplevel__`;
3167
+ if (context.symbols.find((s) => s.id === symbolId)) return;
3168
+ context.symbols.push({
3169
+ id: symbolId,
3170
+ name: "__toplevel__",
3171
+ kind: "function",
3172
+ filePath: context.filePath,
3173
+ startLine: node.startPosition.row + 1,
3174
+ endLine: node.endPosition.row + 1,
3175
+ exported: true
3176
+ });
3177
+ }
3178
+ function processCallExpression7(node, context) {
3179
+ const functionNode = node.childForFieldName("function");
3180
+ if (!functionNode) return;
3181
+ let calleeName = null;
3182
+ if (functionNode.type === "identifier") {
3183
+ calleeName = nodeText6(functionNode, context);
3184
+ } else if (functionNode.type === "member_access_expression") {
3185
+ const nameNode = functionNode.childForFieldName("name");
3186
+ if (nameNode) {
3187
+ calleeName = nodeText6(nameNode, context);
3188
+ }
3189
+ }
3190
+ if (!calleeName) return;
3191
+ const builtins = ["ToString", "Equals", "GetHashCode", "GetType", "Console", "Write", "WriteLine", "Format", "Parse", "TryParse"];
3192
+ if (builtins.includes(calleeName)) return;
3193
+ const callerId = getCurrentSymbolId7(context);
3194
+ if (!callerId) return;
3195
+ const calleeId = resolveSymbol6(calleeName, context);
3196
+ if (calleeId) {
3197
+ context.edges.push({
3198
+ source: callerId,
3199
+ target: calleeId,
3200
+ kind: "calls",
3201
+ filePath: context.filePath,
3202
+ line: node.startPosition.row + 1
3203
+ });
3204
+ }
3205
+ }
3206
+ function parseCsprojFile(filePath, sourceCode, projectRoot) {
3207
+ const symbols = [];
3208
+ const edges = [];
3209
+ const lines = sourceCode.split("\n");
3210
+ const projectName = basename(filePath, ".csproj");
3211
+ symbols.push({
3212
+ id: `${filePath}::${projectName}`,
3213
+ name: projectName,
3214
+ kind: "module",
3215
+ filePath,
3216
+ startLine: 1,
3217
+ endLine: lines.length,
3218
+ exported: true
3219
+ });
3220
+ for (let i = 0; i < lines.length; i++) {
3221
+ const line = lines[i];
3222
+ const lineNum = i + 1;
3223
+ const projectRefMatch = line.match(/<ProjectReference\s+Include\s*=\s*"([^"]+)"/);
3224
+ if (projectRefMatch) {
3225
+ const refPath = projectRefMatch[1];
3226
+ const csprojDir = dirname7(join8(projectRoot, filePath));
3227
+ const resolvedRef = resolve3(csprojDir, refPath);
3228
+ const relativeRef = resolvedRef.startsWith(projectRoot + "/") ? resolvedRef.substring(projectRoot.length + 1) : null;
3229
+ if (relativeRef && existsSync8(resolvedRef)) {
3230
+ edges.push({
3231
+ source: `${filePath}::__file__`,
3232
+ target: `${relativeRef}::__file__`,
3233
+ kind: "imports",
3234
+ filePath,
3235
+ line: lineNum
3236
+ });
3237
+ }
3238
+ }
3239
+ const packageRefMatch = line.match(/<PackageReference\s+Include\s*=\s*"([^"]+)"/);
3240
+ if (packageRefMatch) {
3241
+ const packageName = packageRefMatch[1];
3242
+ const versionMatch = line.match(/Version\s*=\s*"([^"]+)"/);
3243
+ const version = versionMatch ? versionMatch[1] : "unknown";
3244
+ symbols.push({
3245
+ id: `${filePath}::pkg:${packageName}`,
3246
+ name: `${packageName}@${version}`,
3247
+ kind: "import",
3248
+ filePath,
3249
+ startLine: lineNum,
3250
+ endLine: lineNum,
3251
+ exported: false
3252
+ });
3253
+ }
3254
+ }
3255
+ return { filePath, symbols, edges };
3256
+ }
3257
+ function resolveCSharpNamespace(namespace, currentFile, projectRoot) {
3258
+ const namespacePath = namespace.replace(/\./g, "/");
3259
+ const candidates = [
3260
+ join8(projectRoot, namespacePath),
3261
+ join8(projectRoot, "src", namespacePath)
3262
+ ];
3263
+ for (const candidate of candidates) {
3264
+ if (existsSync8(candidate)) {
3265
+ try {
3266
+ const stats = statSync2(candidate);
3267
+ if (stats.isDirectory()) {
3268
+ const csFiles = readdirSync5(candidate).filter((f) => f.endsWith(".cs"));
3269
+ if (csFiles.length > 0) {
3270
+ const fullPath = join8(candidate, csFiles[0]);
3271
+ return fullPath.substring(projectRoot.length + 1);
3272
+ }
3273
+ }
3274
+ } catch {
3275
+ }
3276
+ }
3277
+ }
3278
+ return null;
3279
+ }
3280
+ function resolveSymbol6(name, context) {
3281
+ if (context.imports.has(name)) {
3282
+ return context.imports.get(name) || null;
3283
+ }
3284
+ const currentFileId = `${context.filePath}::${name}`;
3285
+ if (context.symbols.find((s) => s.id === currentFileId)) {
3286
+ return currentFileId;
3287
+ }
3288
+ if (context.currentClass) {
3289
+ const classMethodId = `${context.filePath}::${context.currentClass}.${name}`;
3290
+ if (context.symbols.find((s) => s.id === classMethodId)) {
3291
+ return classMethodId;
3292
+ }
3293
+ }
3294
+ return null;
3295
+ }
3296
+ function hasModifier(node, context, modifier) {
3297
+ for (let i = 0; i < node.childCount; i++) {
3298
+ const child = node.child(i);
3299
+ if (child && child.type === "modifier" && nodeText6(child, context) === modifier) {
3300
+ return true;
3301
+ }
3302
+ }
3303
+ if (modifier === "internal") {
3304
+ const hasExplicitAccess = ["public", "private", "protected", "internal"].some((m) => {
3305
+ for (let i = 0; i < node.childCount; i++) {
3306
+ const child = node.child(i);
3307
+ if (child && child.type === "modifier" && nodeText6(child, context) === m) return true;
3308
+ }
3309
+ return false;
3310
+ });
3311
+ return !hasExplicitAccess;
3312
+ }
3313
+ return false;
3314
+ }
3315
+ function extractBaseTypeName(node, context) {
3316
+ const text = nodeText6(node, context).trim();
3317
+ if (!text || text === ":" || text === ",") return null;
3318
+ const angleBracketIdx = text.indexOf("<");
3319
+ const name = angleBracketIdx > 0 ? text.substring(0, angleBracketIdx) : text;
3320
+ const dotIdx = name.lastIndexOf(".");
3321
+ return dotIdx >= 0 ? name.substring(dotIdx + 1) : name;
3322
+ }
3323
+ function findChildByType7(node, type) {
3324
+ for (let i = 0; i < node.childCount; i++) {
3325
+ const child = node.child(i);
3326
+ if (child && child.type === type) return child;
3327
+ }
3328
+ return null;
3329
+ }
3330
+ function findChildrenByType2(node, type) {
3331
+ const results = [];
3332
+ for (let i = 0; i < node.childCount; i++) {
3333
+ const child = node.child(i);
3334
+ if (child && child.type === type) results.push(child);
3335
+ }
3336
+ return results;
3337
+ }
3338
+ function nodeText6(node, context) {
3339
+ return context.sourceCode.substring(node.startIndex, node.endIndex);
3340
+ }
3341
+ function getCurrentSymbolId7(context) {
3342
+ if (context.currentScope.length === 0) return null;
3343
+ return `${context.filePath}::${context.currentScope[context.currentScope.length - 1]}`;
3344
+ }
3345
+ var csharpParser = {
3346
+ name: "csharp",
3347
+ extensions: [".cs", ".csx", ".csproj"],
3348
+ parseFile: parseCSharpFile
3349
+ };
3350
+
3351
+ // src/parser/java.ts
3352
+ import { dirname as dirname8, join as join9, resolve as resolve4, basename as basename2 } from "path";
3353
+ import { existsSync as existsSync9, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
3354
+ function parseJavaFile(filePath, sourceCode, projectRoot) {
3355
+ if (filePath.endsWith("pom.xml")) {
3356
+ return parsePomXml(filePath, sourceCode, projectRoot);
3357
+ }
3358
+ if (filePath.endsWith("build.gradle") || filePath.endsWith("build.gradle.kts")) {
3359
+ return parseGradleBuild(filePath, sourceCode, projectRoot);
3360
+ }
3361
+ const parser = getParser("java");
3362
+ const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
3363
+ const context = {
3364
+ filePath,
3365
+ projectRoot,
3366
+ sourceCode,
3367
+ symbols: [],
3368
+ edges: [],
3369
+ currentScope: [],
3370
+ currentClass: null,
3371
+ currentPackage: null,
3372
+ imports: /* @__PURE__ */ new Map(),
3373
+ isBuildFile: false
3374
+ };
3375
+ walkNode8(tree.rootNode, context);
3376
+ return {
3377
+ filePath,
3378
+ symbols: context.symbols,
3379
+ edges: context.edges
3380
+ };
3381
+ }
3382
+ function walkNode8(node, context) {
3383
+ processNode8(node, context);
3384
+ for (let i = 0; i < node.childCount; i++) {
3385
+ const child = node.child(i);
3386
+ if (child) {
3387
+ walkNode8(child, context);
3388
+ }
3389
+ }
3390
+ }
3391
+ function processNode8(node, context) {
3392
+ switch (node.type) {
3393
+ case "package_declaration":
3394
+ processPackageDeclaration(node, context);
3395
+ break;
3396
+ case "import_declaration":
3397
+ processImportDeclaration2(node, context);
3398
+ break;
3399
+ case "class_declaration":
3400
+ processClassDeclaration4(node, context);
3401
+ break;
3402
+ case "interface_declaration":
3403
+ processInterfaceDeclaration3(node, context);
3404
+ break;
3405
+ case "enum_declaration":
3406
+ processEnumDeclaration3(node, context);
3407
+ break;
3408
+ case "annotation_type_declaration":
3409
+ processAnnotationTypeDeclaration(node, context);
3410
+ break;
3411
+ case "record_declaration":
3412
+ processRecordDeclaration2(node, context);
3413
+ break;
3414
+ case "method_declaration":
3415
+ processMethodDeclaration3(node, context);
3416
+ break;
3417
+ case "constructor_declaration":
3418
+ processConstructorDeclaration2(node, context);
3419
+ break;
3420
+ case "field_declaration":
3421
+ processFieldDeclaration(node, context);
3422
+ break;
3423
+ case "constant_declaration":
3424
+ processConstantDeclaration(node, context);
3425
+ break;
3426
+ case "annotation_type_element_declaration":
3427
+ processAnnotationElement(node, context);
3428
+ break;
3429
+ case "method_invocation":
3430
+ processCallExpression8(node, context);
3431
+ break;
3432
+ case "object_creation_expression":
3433
+ processObjectCreation(node, context);
3434
+ break;
3435
+ case "lambda_expression":
3436
+ processLambdaExpression(node, context);
3437
+ break;
3438
+ }
3439
+ }
3440
+ function processPackageDeclaration(node, context) {
3441
+ const scopedIdent = findDescendantByTypes(node, ["scoped_identifier", "identifier"]);
3442
+ if (!scopedIdent) return;
3443
+ const name = nodeText7(scopedIdent, context);
3444
+ const symbolId = `${context.filePath}::${name}`;
3445
+ context.symbols.push({
3446
+ id: symbolId,
3447
+ name,
3448
+ kind: "module",
3449
+ filePath: context.filePath,
3450
+ startLine: node.startPosition.row + 1,
3451
+ endLine: node.endPosition.row + 1,
3452
+ exported: true
3453
+ });
3454
+ context.currentPackage = name;
3455
+ }
3456
+ function processImportDeclaration2(node, context) {
3457
+ const text = nodeText7(node, context).trim();
3458
+ const isStatic = text.includes("import static");
3459
+ const isWildcard = text.includes(".*");
3460
+ const scopedIdent = findDescendantByTypes(node, ["scoped_identifier", "identifier"]);
3461
+ if (!scopedIdent) return;
3462
+ let importPath = nodeText7(scopedIdent, context);
3463
+ const asterisk = findChildByType8(node, "asterisk");
3464
+ if (asterisk) {
3465
+ importPath = importPath + ".*";
3466
+ }
3467
+ const resolvedPath = resolveJavaImport(importPath, context.filePath, context.projectRoot);
3468
+ if (resolvedPath) {
3469
+ const sourceId = `${context.filePath}::__file__`;
3470
+ const targetId = `${resolvedPath}::__file__`;
3471
+ context.edges.push({
3472
+ source: sourceId,
3473
+ target: targetId,
3474
+ kind: "imports",
3475
+ filePath: context.filePath,
3476
+ line: node.startPosition.row + 1
3477
+ });
3478
+ const parts = importPath.split(".");
3479
+ if (!isWildcard) {
3480
+ const simpleName = parts[parts.length - 1];
3481
+ context.imports.set(simpleName, `${resolvedPath}::${simpleName}`);
3482
+ }
3483
+ }
3484
+ const symbolId = `${context.filePath}::import:${importPath}`;
3485
+ context.symbols.push({
3486
+ id: symbolId,
3487
+ name: importPath,
3488
+ kind: "import",
3489
+ filePath: context.filePath,
3490
+ startLine: node.startPosition.row + 1,
3491
+ endLine: node.endPosition.row + 1,
3492
+ exported: false
3493
+ });
3494
+ }
3495
+ function processClassDeclaration4(node, context) {
3496
+ processTypeDeclaration3(node, context, "class");
3497
+ }
3498
+ function processInterfaceDeclaration3(node, context) {
3499
+ processTypeDeclaration3(node, context, "interface");
3500
+ }
3501
+ function processRecordDeclaration2(node, context) {
3502
+ processTypeDeclaration3(node, context, "class");
3503
+ }
3504
+ function processAnnotationTypeDeclaration(node, context) {
3505
+ processTypeDeclaration3(node, context, "interface");
3506
+ }
3507
+ function processTypeDeclaration3(node, context, kind) {
3508
+ const nameNode = node.childForFieldName("name");
3509
+ if (!nameNode) return;
3510
+ let name = nodeText7(nameNode, context);
3511
+ const angleBracketIdx = name.indexOf("<");
3512
+ if (angleBracketIdx > 0) {
3513
+ name = name.substring(0, angleBracketIdx);
3514
+ }
3515
+ const exported = hasModifier2(node, context, "public");
3516
+ const scope = context.currentClass || void 0;
3517
+ const symbolId = `${context.filePath}::${name}`;
3518
+ context.symbols.push({
3519
+ id: symbolId,
3520
+ name,
3521
+ kind,
3522
+ filePath: context.filePath,
3523
+ startLine: node.startPosition.row + 1,
3524
+ endLine: node.endPosition.row + 1,
3525
+ exported,
3526
+ scope
3527
+ });
3528
+ const superclass = node.childForFieldName("superclass");
3529
+ if (superclass) {
3530
+ let baseName = extractTypeName3(superclass, context);
3531
+ if (baseName) {
3532
+ const baseId = resolveSymbol7(baseName, context);
3533
+ if (baseId) {
3534
+ context.edges.push({
3535
+ source: symbolId,
3536
+ target: baseId,
3537
+ kind: "inherits",
3538
+ filePath: context.filePath,
3539
+ line: superclass.startPosition.row + 1
3540
+ });
3541
+ }
3542
+ }
3543
+ }
3544
+ const interfaces = node.childForFieldName("interfaces");
3545
+ if (interfaces) {
3546
+ processInterfaceList(interfaces, symbolId, context);
3547
+ }
3548
+ const extendsInterfaces = node.childForFieldName("extends_interfaces") || findChildByType8(node, "extends_interfaces");
3549
+ if (extendsInterfaces) {
3550
+ processInterfaceList(extendsInterfaces, symbolId, context);
3551
+ }
3552
+ const oldClass = context.currentClass;
3553
+ context.currentClass = name;
3554
+ context.currentScope.push(name);
3555
+ const body = node.childForFieldName("body") || findChildByType8(node, "class_body") || findChildByType8(node, "interface_body") || findChildByType8(node, "enum_body") || findChildByType8(node, "annotation_type_body") || findChildByType8(node, "record_declaration_body");
3556
+ if (body) {
3557
+ walkNode8(body, context);
3558
+ }
3559
+ context.currentScope.pop();
3560
+ context.currentClass = oldClass;
3561
+ }
3562
+ function processInterfaceList(node, sourceId, context) {
3563
+ for (let i = 0; i < node.childCount; i++) {
3564
+ const child = node.child(i);
3565
+ if (!child) continue;
3566
+ if (child.type === "type_identifier" || child.type === "generic_type" || child.type === "scoped_type_identifier") {
3567
+ const baseName = extractTypeName3(child, context);
3568
+ if (baseName) {
3569
+ const baseId = resolveSymbol7(baseName, context);
3570
+ if (baseId) {
3571
+ context.edges.push({
3572
+ source: sourceId,
3573
+ target: baseId,
3574
+ kind: "implements",
3575
+ filePath: context.filePath,
3576
+ line: child.startPosition.row + 1
3577
+ });
3578
+ }
3579
+ }
3580
+ }
3581
+ }
3582
+ }
3583
+ function processEnumDeclaration3(node, context) {
3584
+ const nameNode = node.childForFieldName("name");
3585
+ if (!nameNode) return;
3586
+ const name = nodeText7(nameNode, context);
3587
+ const exported = hasModifier2(node, context, "public");
3588
+ const symbolId = `${context.filePath}::${name}`;
3589
+ context.symbols.push({
3590
+ id: symbolId,
3591
+ name,
3592
+ kind: "enum",
3593
+ filePath: context.filePath,
3594
+ startLine: node.startPosition.row + 1,
3595
+ endLine: node.endPosition.row + 1,
3596
+ exported
3597
+ });
3598
+ const body = node.childForFieldName("body") || findChildByType8(node, "enum_body");
3599
+ if (body) {
3600
+ const constants = findChildrenByType3(body, "enum_constant");
3601
+ for (const constant of constants) {
3602
+ const constNameNode = constant.childForFieldName("name");
3603
+ if (!constNameNode) continue;
3604
+ const constName = nodeText7(constNameNode, context);
3605
+ const constId = `${context.filePath}::${name}.${constName}`;
3606
+ context.symbols.push({
3607
+ id: constId,
3608
+ name: constName,
3609
+ kind: "constant",
3610
+ filePath: context.filePath,
3611
+ startLine: constant.startPosition.row + 1,
3612
+ endLine: constant.endPosition.row + 1,
3613
+ exported,
3614
+ scope: name
3615
+ });
3616
+ }
3617
+ const oldClass = context.currentClass;
3618
+ context.currentClass = name;
3619
+ context.currentScope.push(name);
3620
+ for (let i = 0; i < body.childCount; i++) {
3621
+ const child = body.child(i);
3622
+ if (child && child.type !== "enum_constant") {
3623
+ walkNode8(child, context);
3624
+ }
3625
+ }
3626
+ context.currentScope.pop();
3627
+ context.currentClass = oldClass;
3628
+ }
3629
+ }
3630
+ function processMethodDeclaration3(node, context) {
3631
+ const nameNode = node.childForFieldName("name");
3632
+ if (!nameNode) return;
3633
+ const name = nodeText7(nameNode, context);
3634
+ const exported = hasModifier2(node, context, "public");
3635
+ const scope = context.currentClass || void 0;
3636
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3637
+ context.symbols.push({
3638
+ id: symbolId,
3639
+ name,
3640
+ kind: context.currentClass ? "method" : "function",
3641
+ filePath: context.filePath,
3642
+ startLine: node.startPosition.row + 1,
3643
+ endLine: node.endPosition.row + 1,
3644
+ exported,
3645
+ scope
3646
+ });
3647
+ const scopeName = scope ? `${scope}.${name}` : name;
3648
+ context.currentScope.push(scopeName);
3649
+ const body = node.childForFieldName("body");
3650
+ if (body) {
3651
+ walkNode8(body, context);
3652
+ }
3653
+ context.currentScope.pop();
3654
+ }
3655
+ function processConstructorDeclaration2(node, context) {
3656
+ const nameNode = node.childForFieldName("name");
3657
+ if (!nameNode) return;
3658
+ const name = nodeText7(nameNode, context);
3659
+ const exported = hasModifier2(node, context, "public");
3660
+ const scope = context.currentClass || void 0;
3661
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3662
+ context.symbols.push({
3663
+ id: symbolId,
3664
+ name,
3665
+ kind: "method",
3666
+ filePath: context.filePath,
3667
+ startLine: node.startPosition.row + 1,
3668
+ endLine: node.endPosition.row + 1,
3669
+ exported,
3670
+ scope
3671
+ });
3672
+ const scopeName = scope ? `${scope}.${name}` : name;
3673
+ context.currentScope.push(scopeName);
3674
+ const body = node.childForFieldName("body");
3675
+ if (body) {
3676
+ walkNode8(body, context);
3677
+ }
3678
+ context.currentScope.pop();
3679
+ }
3680
+ function processFieldDeclaration(node, context) {
3681
+ const declarator = findDescendantByTypes(node, ["variable_declarator"]);
3682
+ if (!declarator) return;
3683
+ const nameNode = declarator.childForFieldName("name");
3684
+ if (!nameNode) return;
3685
+ const name = nodeText7(nameNode, context);
3686
+ const exported = hasModifier2(node, context, "public");
3687
+ const scope = context.currentClass || void 0;
3688
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3689
+ const isConstant = hasModifier2(node, context, "static") && hasModifier2(node, context, "final");
3690
+ context.symbols.push({
3691
+ id: symbolId,
3692
+ name,
3693
+ kind: isConstant ? "constant" : "property",
3694
+ filePath: context.filePath,
3695
+ startLine: node.startPosition.row + 1,
3696
+ endLine: node.endPosition.row + 1,
3697
+ exported,
3698
+ scope
3699
+ });
3700
+ }
3701
+ function processConstantDeclaration(node, context) {
3702
+ const declarator = findDescendantByTypes(node, ["variable_declarator"]);
3703
+ if (!declarator) return;
3704
+ const nameNode = declarator.childForFieldName("name");
3705
+ if (!nameNode) return;
3706
+ const name = nodeText7(nameNode, context);
3707
+ const scope = context.currentClass || void 0;
3708
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3709
+ context.symbols.push({
3710
+ id: symbolId,
3711
+ name,
3712
+ kind: "constant",
3713
+ filePath: context.filePath,
3714
+ startLine: node.startPosition.row + 1,
3715
+ endLine: node.endPosition.row + 1,
3716
+ exported: true,
3717
+ // Interface constants are always public
3718
+ scope
3719
+ });
3720
+ }
3721
+ function processAnnotationElement(node, context) {
3722
+ const nameNode = node.childForFieldName("name");
3723
+ if (!nameNode) return;
3724
+ const name = nodeText7(nameNode, context);
3725
+ const scope = context.currentClass || void 0;
3726
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3727
+ context.symbols.push({
3728
+ id: symbolId,
3729
+ name,
3730
+ kind: "method",
3731
+ filePath: context.filePath,
3732
+ startLine: node.startPosition.row + 1,
3733
+ endLine: node.endPosition.row + 1,
3734
+ exported: true,
3735
+ scope
3736
+ });
3737
+ }
3738
+ function processCallExpression8(node, context) {
3739
+ const nameNode = node.childForFieldName("name");
3740
+ if (!nameNode) return;
3741
+ const calleeName = nodeText7(nameNode, context);
3742
+ const builtins = [
3743
+ "toString",
3744
+ "equals",
3745
+ "hashCode",
3746
+ "getClass",
3747
+ "println",
3748
+ "printf",
3749
+ "format",
3750
+ "parseInt",
3751
+ "valueOf",
3752
+ "length",
3753
+ "size",
3754
+ "get",
3755
+ "set",
3756
+ "add",
3757
+ "remove",
3758
+ "contains",
3759
+ "isEmpty",
3760
+ "stream",
3761
+ "collect",
3762
+ "map",
3763
+ "filter",
3764
+ "forEach",
3765
+ "of",
3766
+ "orElse",
3767
+ "orElseThrow",
3768
+ "isPresent",
3769
+ "ifPresent",
3770
+ "close",
3771
+ "flush",
3772
+ "write",
3773
+ "read"
3774
+ ];
3775
+ if (builtins.includes(calleeName)) return;
3776
+ const callerId = getCurrentSymbolId8(context);
3777
+ if (!callerId) return;
3778
+ const calleeId = resolveSymbol7(calleeName, context);
3779
+ if (calleeId) {
3780
+ context.edges.push({
3781
+ source: callerId,
3782
+ target: calleeId,
3783
+ kind: "calls",
3784
+ filePath: context.filePath,
3785
+ line: node.startPosition.row + 1
3786
+ });
3787
+ }
3788
+ }
3789
+ function processObjectCreation(node, context) {
3790
+ const typeNode = node.childForFieldName("type");
3791
+ if (!typeNode) return;
3792
+ const typeName = extractTypeName3(typeNode, context);
3793
+ if (!typeName) return;
3794
+ const callerId = getCurrentSymbolId8(context);
3795
+ if (!callerId) return;
3796
+ const targetId = resolveSymbol7(typeName, context);
3797
+ if (targetId) {
3798
+ context.edges.push({
3799
+ source: callerId,
3800
+ target: targetId,
3801
+ kind: "references",
3802
+ filePath: context.filePath,
3803
+ line: node.startPosition.row + 1
3804
+ });
3805
+ }
3806
+ const classBody = findChildByType8(node, "class_body");
3807
+ if (classBody) {
3808
+ const anonName = `<anonymous:${typeName}>`;
3809
+ const anonId = `${context.filePath}::${anonName}:${node.startPosition.row + 1}`;
3810
+ context.symbols.push({
3811
+ id: anonId,
3812
+ name: anonName,
3813
+ kind: "class",
3814
+ filePath: context.filePath,
3815
+ startLine: node.startPosition.row + 1,
3816
+ endLine: node.endPosition.row + 1,
3817
+ exported: false,
3818
+ scope: context.currentClass || void 0
3819
+ });
3820
+ const oldClass = context.currentClass;
3821
+ context.currentClass = anonName;
3822
+ context.currentScope.push(anonName);
3823
+ walkNode8(classBody, context);
3824
+ context.currentScope.pop();
3825
+ context.currentClass = oldClass;
3826
+ }
3827
+ }
3828
+ function processLambdaExpression(node, context) {
3829
+ const parent = node.parent;
3830
+ if (!parent) return;
3831
+ if (parent.type === "variable_declarator") {
3832
+ const nameNode = parent.childForFieldName("name");
3833
+ if (nameNode) {
3834
+ const name = nodeText7(nameNode, context);
3835
+ const scope = context.currentClass || void 0;
3836
+ const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
3837
+ context.symbols.push({
3838
+ id: symbolId,
3839
+ name,
3840
+ kind: "function",
3841
+ filePath: context.filePath,
3842
+ startLine: node.startPosition.row + 1,
3843
+ endLine: node.endPosition.row + 1,
3844
+ exported: false,
3845
+ scope
3846
+ });
3847
+ }
3848
+ }
3849
+ }
3850
+ function parsePomXml(filePath, sourceCode, projectRoot) {
3851
+ const symbols = [];
3852
+ const edges = [];
3853
+ const lines = sourceCode.split("\n");
3854
+ const projectName = basename2(dirname8(join9(projectRoot, filePath)));
3855
+ symbols.push({
3856
+ id: `${filePath}::${projectName}`,
3857
+ name: projectName,
3858
+ kind: "module",
3859
+ filePath,
3860
+ startLine: 1,
3861
+ endLine: lines.length,
3862
+ exported: true
3863
+ });
3864
+ let inDependency = false;
3865
+ let groupId = "";
3866
+ let artifactId = "";
3867
+ let version = "";
3868
+ let depStartLine = 0;
3869
+ for (let i = 0; i < lines.length; i++) {
3870
+ const line = lines[i];
3871
+ const lineNum = i + 1;
3872
+ if (/<dependency>/.test(line)) {
3873
+ inDependency = true;
3874
+ groupId = "";
3875
+ artifactId = "";
3876
+ version = "";
3877
+ depStartLine = lineNum;
3878
+ }
3879
+ if (inDependency) {
3880
+ const gMatch = line.match(/<groupId>([^<]+)<\/groupId>/);
3881
+ if (gMatch) groupId = gMatch[1];
3882
+ const aMatch = line.match(/<artifactId>([^<]+)<\/artifactId>/);
3883
+ if (aMatch) artifactId = aMatch[1];
3884
+ const vMatch = line.match(/<version>([^<]+)<\/version>/);
3885
+ if (vMatch) version = vMatch[1];
3886
+ }
3887
+ if (/<\/dependency>/.test(line) && inDependency) {
3888
+ inDependency = false;
3889
+ if (groupId && artifactId) {
3890
+ const depName = `${groupId}:${artifactId}`;
3891
+ const displayVersion = version || "managed";
3892
+ symbols.push({
3893
+ id: `${filePath}::dep:${depName}`,
3894
+ name: `${depName}@${displayVersion}`,
3895
+ kind: "import",
3896
+ filePath,
3897
+ startLine: depStartLine,
3898
+ endLine: lineNum,
3899
+ exported: false
3900
+ });
3901
+ }
3902
+ }
3903
+ const moduleMatch = line.match(/<module>([^<]+)<\/module>/);
3904
+ if (moduleMatch) {
3905
+ const modulePath = moduleMatch[1];
3906
+ const pomDir = dirname8(join9(projectRoot, filePath));
3907
+ const resolvedModule = resolve4(pomDir, modulePath);
3908
+ const relativeModule = resolvedModule.startsWith(projectRoot + "/") ? resolvedModule.substring(projectRoot.length + 1) : null;
3909
+ if (relativeModule) {
3910
+ const modulePom = join9(relativeModule, "pom.xml");
3911
+ if (existsSync9(join9(projectRoot, modulePom))) {
3912
+ edges.push({
3913
+ source: `${filePath}::__file__`,
3914
+ target: `${modulePom}::__file__`,
3915
+ kind: "imports",
3916
+ filePath,
3917
+ line: lineNum
3918
+ });
3919
+ }
3920
+ }
3921
+ }
3922
+ }
3923
+ return { filePath, symbols, edges };
3924
+ }
3925
+ function parseGradleBuild(filePath, sourceCode, projectRoot) {
3926
+ const symbols = [];
3927
+ const edges = [];
3928
+ const lines = sourceCode.split("\n");
3929
+ const projectName = basename2(dirname8(join9(projectRoot, filePath)));
3930
+ symbols.push({
3931
+ id: `${filePath}::${projectName}`,
3932
+ name: projectName,
3933
+ kind: "module",
3934
+ filePath,
3935
+ startLine: 1,
3936
+ endLine: lines.length,
3937
+ exported: true
3938
+ });
3939
+ for (let i = 0; i < lines.length; i++) {
3940
+ const line = lines[i].trim();
3941
+ const lineNum = i + 1;
3942
+ const depMatch = line.match(
3943
+ /(?:implementation|api|compileOnly|runtimeOnly|testImplementation|testRuntimeOnly|annotationProcessor)\s*[\(]?\s*['"]([^'"]+)['"]\s*[\)]?/
3944
+ );
3945
+ if (depMatch) {
3946
+ const depCoord = depMatch[1];
3947
+ if (!depCoord.startsWith(":")) {
3948
+ symbols.push({
3949
+ id: `${filePath}::dep:${depCoord}`,
3950
+ name: depCoord,
3951
+ kind: "import",
3952
+ filePath,
3953
+ startLine: lineNum,
3954
+ endLine: lineNum,
3955
+ exported: false
3956
+ });
3957
+ }
3958
+ }
3959
+ const projectMatch = line.match(/project\s*\(\s*['":]+([^'")\s]+)['"]*\s*\)/);
3960
+ if (projectMatch) {
3961
+ const moduleName = projectMatch[1].replace(/^:/, "");
3962
+ const candidates = [
3963
+ join9(moduleName, "build.gradle"),
3964
+ join9(moduleName, "build.gradle.kts")
3965
+ ];
3966
+ for (const candidate of candidates) {
3967
+ if (existsSync9(join9(projectRoot, candidate))) {
3968
+ edges.push({
3969
+ source: `${filePath}::__file__`,
3970
+ target: `${candidate}::__file__`,
3971
+ kind: "imports",
3972
+ filePath,
3973
+ line: lineNum
3974
+ });
3975
+ break;
3976
+ }
3977
+ }
3978
+ }
3979
+ }
3980
+ return { filePath, symbols, edges };
3981
+ }
3982
+ function resolveJavaImport(importPath, currentFile, projectRoot) {
3983
+ const cleanPath2 = importPath.replace(/\.\*$/, "");
3984
+ const javaPath = cleanPath2.replace(/\./g, "/") + ".java";
3985
+ const sourceRoots = [
3986
+ "",
3987
+ "src/main/java",
3988
+ "src",
3989
+ "app/src/main/java"
3990
+ ];
3991
+ for (const root of sourceRoots) {
3992
+ const candidate = root ? join9(root, javaPath) : javaPath;
3993
+ const fullPath = join9(projectRoot, candidate);
3994
+ if (existsSync9(fullPath)) {
3995
+ return candidate;
3996
+ }
3997
+ }
3998
+ if (importPath.endsWith(".*")) {
3999
+ const packagePath = cleanPath2.replace(/\./g, "/");
4000
+ for (const root of sourceRoots) {
4001
+ const candidate = root ? join9(root, packagePath) : packagePath;
4002
+ const fullPath = join9(projectRoot, candidate);
4003
+ if (existsSync9(fullPath)) {
4004
+ try {
4005
+ const stats = statSync3(fullPath);
4006
+ if (stats.isDirectory()) {
4007
+ const javaFiles = readdirSync6(fullPath).filter((f) => f.endsWith(".java"));
4008
+ if (javaFiles.length > 0) {
4009
+ return join9(candidate, javaFiles[0]);
4010
+ }
4011
+ }
4012
+ } catch {
4013
+ }
4014
+ }
4015
+ }
4016
+ }
4017
+ return null;
4018
+ }
4019
+ function resolveSymbol7(name, context) {
4020
+ if (context.imports.has(name)) {
4021
+ return context.imports.get(name) || null;
4022
+ }
4023
+ const currentFileId = `${context.filePath}::${name}`;
4024
+ if (context.symbols.find((s) => s.id === currentFileId)) {
4025
+ return currentFileId;
4026
+ }
4027
+ if (context.currentClass) {
4028
+ const classMethodId = `${context.filePath}::${context.currentClass}.${name}`;
4029
+ if (context.symbols.find((s) => s.id === classMethodId)) {
4030
+ return classMethodId;
4031
+ }
4032
+ }
4033
+ return null;
4034
+ }
4035
+ function hasModifier2(node, context, modifier) {
4036
+ const modifiers = node.childForFieldName("modifiers") || findChildByType8(node, "modifiers");
4037
+ if (modifiers) {
4038
+ for (let i = 0; i < modifiers.childCount; i++) {
4039
+ const child = modifiers.child(i);
4040
+ if (child && nodeText7(child, context) === modifier) {
4041
+ return true;
4042
+ }
4043
+ }
4044
+ }
4045
+ for (let i = 0; i < node.childCount; i++) {
4046
+ const child = node.child(i);
4047
+ if (child && child.type === modifier) {
4048
+ return true;
4049
+ }
4050
+ }
4051
+ return false;
4052
+ }
4053
+ function extractTypeName3(node, context) {
4054
+ const text = nodeText7(node, context).trim();
4055
+ if (!text) return null;
4056
+ const angleBracketIdx = text.indexOf("<");
4057
+ const name = angleBracketIdx > 0 ? text.substring(0, angleBracketIdx) : text;
4058
+ const dotIdx = name.lastIndexOf(".");
4059
+ return dotIdx >= 0 ? name.substring(dotIdx + 1) : name;
4060
+ }
4061
+ function findChildByType8(node, type) {
4062
+ for (let i = 0; i < node.childCount; i++) {
4063
+ const child = node.child(i);
4064
+ if (child && child.type === type) return child;
4065
+ }
4066
+ return null;
4067
+ }
4068
+ function findChildrenByType3(node, type) {
4069
+ const results = [];
4070
+ for (let i = 0; i < node.childCount; i++) {
4071
+ const child = node.child(i);
4072
+ if (child && child.type === type) results.push(child);
4073
+ }
4074
+ return results;
4075
+ }
4076
+ function findDescendantByTypes(node, types) {
4077
+ for (let i = 0; i < node.childCount; i++) {
4078
+ const child = node.child(i);
4079
+ if (!child) continue;
4080
+ if (types.includes(child.type)) return child;
4081
+ const found = findDescendantByTypes(child, types);
4082
+ if (found) return found;
4083
+ }
4084
+ return null;
4085
+ }
4086
+ function nodeText7(node, context) {
4087
+ return context.sourceCode.substring(node.startIndex, node.endIndex);
4088
+ }
4089
+ function getCurrentSymbolId8(context) {
4090
+ if (context.currentScope.length === 0) return null;
4091
+ return `${context.filePath}::${context.currentScope[context.currentScope.length - 1]}`;
4092
+ }
4093
+ var javaParser = {
4094
+ name: "java",
4095
+ extensions: [".java", "pom.xml", "build.gradle", "build.gradle.kts"],
4096
+ parseFile: parseJavaFile
4097
+ };
4098
+
2785
4099
  // src/parser/detect.ts
2786
4100
  var parsers = [
2787
4101
  typescriptParser,
@@ -2789,11 +4103,14 @@ var parsers = [
2789
4103
  javascriptParser,
2790
4104
  goParser,
2791
4105
  rustParser,
2792
- cParser
4106
+ cParser,
4107
+ csharpParser,
4108
+ javaParser
2793
4109
  ];
2794
4110
  function getParserForFile(filePath) {
2795
- const ext = extname3(filePath).toLowerCase();
2796
- return parsers.find((p) => p.extensions.includes(ext)) || null;
4111
+ const ext = extname5(filePath).toLowerCase();
4112
+ const fileName = basename3(filePath);
4113
+ return parsers.find((p) => p.extensions.includes(ext) || p.extensions.includes(fileName)) || null;
2797
4114
  }
2798
4115
 
2799
4116
  // src/parser/index.ts
@@ -2801,7 +4118,7 @@ import { minimatch } from "minimatch";
2801
4118
  var MAX_FILE_SIZE = 1e6;
2802
4119
  function shouldParseFile(fullPath) {
2803
4120
  try {
2804
- const stats = statSync2(fullPath);
4121
+ const stats = statSync4(fullPath);
2805
4122
  if (stats.size > MAX_FILE_SIZE) {
2806
4123
  console.error(`[Parser] Skipping ${fullPath} \u2014 file too large (${(stats.size / 1024).toFixed(0)}KB)`);
2807
4124
  return false;
@@ -2819,8 +4136,8 @@ async function parseProject(projectRoot, options) {
2819
4136
  let errorFiles = 0;
2820
4137
  for (const file of files) {
2821
4138
  try {
2822
- const fullPath = join8(projectRoot, file);
2823
- if (!resolve3(fullPath).startsWith(resolve3(projectRoot))) {
4139
+ const fullPath = join10(projectRoot, file);
4140
+ if (!resolve5(fullPath).startsWith(resolve5(projectRoot))) {
2824
4141
  skippedFiles++;
2825
4142
  continue;
2826
4143
  }
@@ -2849,7 +4166,7 @@ async function parseProject(projectRoot, options) {
2849
4166
  skippedFiles++;
2850
4167
  continue;
2851
4168
  }
2852
- const sourceCode = readFileSync5(fullPath, "utf-8");
4169
+ const sourceCode = readFileSync7(fullPath, "utf-8");
2853
4170
  const parsed = parser.parseFile(file, sourceCode, projectRoot);
2854
4171
  parsedFiles.push(parsed);
2855
4172
  } catch (err) {
@@ -2872,13 +4189,15 @@ async function parseProject(projectRoot, options) {
2872
4189
  }
2873
4190
 
2874
4191
  // src/cross-language/detectors/rest-api.ts
2875
- import { readFileSync as readFileSync6 } from "fs";
2876
- import { join as join9, resolve as resolve4 } from "path";
4192
+ import { readFileSync as readFileSync8 } from "fs";
4193
+ import { join as join11, resolve as resolve6 } from "path";
2877
4194
  function getLanguage(filePath) {
2878
4195
  if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
2879
4196
  if (filePath.endsWith(".js") || filePath.endsWith(".jsx") || filePath.endsWith(".mjs") || filePath.endsWith(".cjs")) return "javascript";
2880
4197
  if (filePath.endsWith(".py")) return "python";
2881
4198
  if (filePath.endsWith(".go")) return "go";
4199
+ if (filePath.endsWith(".cs") || filePath.endsWith(".csx")) return "csharp";
4200
+ if (filePath.endsWith(".java")) return "java";
2882
4201
  return "unknown";
2883
4202
  }
2884
4203
  function normalizePath(routePath) {
@@ -3015,9 +4334,136 @@ function extractRouteDefinitions(source, filePath) {
3015
4334
  }
3016
4335
  }
3017
4336
  }
4337
+ if (lang === "csharp") {
4338
+ const attrMatch = line.match(/\[\s*Http(Get|Post|Put|Delete|Patch)\s*\(\s*"([^"]+)"\s*\)\s*\]/);
4339
+ if (attrMatch) {
4340
+ routes.push({
4341
+ method: attrMatch[1].toUpperCase(),
4342
+ path: attrMatch[2],
4343
+ normalizedPath: normalizePath(attrMatch[2]),
4344
+ file: filePath,
4345
+ line: i + 1
4346
+ });
4347
+ }
4348
+ const routeAttrMatch = line.match(/\[\s*Route\s*\(\s*"([^"]+)"\s*\)\s*\]/);
4349
+ if (routeAttrMatch) {
4350
+ let routePath = routeAttrMatch[1];
4351
+ if (routePath.includes("[controller]")) {
4352
+ const classMatch = source.match(/class\s+(\w+?)Controller\s/);
4353
+ if (classMatch) {
4354
+ routePath = routePath.replace("[controller]", classMatch[1].toLowerCase());
4355
+ }
4356
+ }
4357
+ if (!routePath.startsWith("/")) routePath = "/" + routePath;
4358
+ routes.push({
4359
+ method: "ANY",
4360
+ path: routePath,
4361
+ normalizedPath: normalizePath(routePath),
4362
+ file: filePath,
4363
+ line: i + 1
4364
+ });
4365
+ }
4366
+ const minimalMatch = line.match(/app\s*\.\s*Map(Get|Post|Put|Delete|Patch)\s*\(\s*"([^"]+)"/);
4367
+ if (minimalMatch) {
4368
+ const path6 = minimalMatch[2];
4369
+ if (path6.startsWith("/")) {
4370
+ routes.push({
4371
+ method: minimalMatch[1].toUpperCase(),
4372
+ path: path6,
4373
+ normalizedPath: normalizePath(path6),
4374
+ file: filePath,
4375
+ line: i + 1
4376
+ });
4377
+ }
4378
+ }
4379
+ }
4380
+ if (lang === "java") {
4381
+ const springMethodMatch = line.match(/@(Get|Post|Put|Delete|Patch)Mapping\s*\(\s*(?:value\s*=\s*)?["']([^"']+)["']/);
4382
+ if (springMethodMatch) {
4383
+ const method = springMethodMatch[1].toUpperCase();
4384
+ let path6 = springMethodMatch[2];
4385
+ const classPrefix = findClassLevelPrefix(source);
4386
+ if (classPrefix) path6 = classPrefix + path6;
4387
+ if (!path6.startsWith("/")) path6 = "/" + path6;
4388
+ routes.push({
4389
+ method,
4390
+ path: path6,
4391
+ normalizedPath: normalizePath(path6),
4392
+ file: filePath,
4393
+ line: i + 1
4394
+ });
4395
+ }
4396
+ if (!springMethodMatch) {
4397
+ const springNoPathMatch = line.match(/@(Get|Post|Put|Delete|Patch)Mapping\s*$/);
4398
+ if (springNoPathMatch) {
4399
+ const method = springNoPathMatch[1].toUpperCase();
4400
+ const classPrefix = findClassLevelPrefix(source);
4401
+ if (classPrefix) {
4402
+ routes.push({
4403
+ method,
4404
+ path: classPrefix,
4405
+ normalizedPath: normalizePath(classPrefix),
4406
+ file: filePath,
4407
+ line: i + 1
4408
+ });
4409
+ }
4410
+ }
4411
+ }
4412
+ const requestMappingMatch = line.match(/@RequestMapping\s*\(\s*(?:value\s*=\s*)?["']([^"']+)["']/);
4413
+ if (requestMappingMatch) {
4414
+ let path6 = requestMappingMatch[1];
4415
+ if (!path6.startsWith("/")) path6 = "/" + path6;
4416
+ const methodMatch = line.match(/method\s*=\s*RequestMethod\.(\w+)/);
4417
+ const method = methodMatch ? methodMatch[1].toUpperCase() : "ANY";
4418
+ routes.push({
4419
+ method,
4420
+ path: path6,
4421
+ normalizedPath: normalizePath(path6),
4422
+ file: filePath,
4423
+ line: i + 1
4424
+ });
4425
+ }
4426
+ const jaxPathMatch = line.match(/@Path\s*\(\s*["']([^"']+)["']\s*\)/);
4427
+ if (jaxPathMatch) {
4428
+ let path6 = jaxPathMatch[1];
4429
+ if (!path6.startsWith("/")) path6 = "/" + path6;
4430
+ const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
4431
+ const prevLine = i > 0 ? lines[i - 1] : "";
4432
+ const jaxMethodMatch = (nextLine + prevLine).match(/@(GET|POST|PUT|DELETE|PATCH)/);
4433
+ const method = jaxMethodMatch ? jaxMethodMatch[1] : "ANY";
4434
+ routes.push({
4435
+ method,
4436
+ path: path6,
4437
+ normalizedPath: normalizePath(path6),
4438
+ file: filePath,
4439
+ line: i + 1
4440
+ });
4441
+ }
4442
+ const webFluxMatch = line.match(/(?:route|andRoute)\s*\(\s*(GET|POST|PUT|DELETE|PATCH)\s*\(\s*["']([^"']+)["']\s*\)/);
4443
+ if (webFluxMatch) {
4444
+ const path6 = webFluxMatch[2].startsWith("/") ? webFluxMatch[2] : "/" + webFluxMatch[2];
4445
+ routes.push({
4446
+ method: webFluxMatch[1].toUpperCase(),
4447
+ path: path6,
4448
+ normalizedPath: normalizePath(path6),
4449
+ file: filePath,
4450
+ line: i + 1
4451
+ });
4452
+ }
4453
+ }
3018
4454
  }
3019
4455
  return routes;
3020
4456
  }
4457
+ function findClassLevelPrefix(source) {
4458
+ const match = source.match(/@RequestMapping\s*\(\s*(?:value\s*=\s*)?["']([^"']+)["']/);
4459
+ if (match) {
4460
+ let path6 = match[1];
4461
+ if (!path6.startsWith("/")) path6 = "/" + path6;
4462
+ if (path6.endsWith("/") && path6.length > 1) path6 = path6.slice(0, -1);
4463
+ return path6;
4464
+ }
4465
+ return null;
4466
+ }
3021
4467
  function matchPaths(callPath, routeNormalized) {
3022
4468
  const normalizedCall = normalizePath(stripTrailingSlash(callPath));
3023
4469
  const normalizedRoute = stripTrailingSlash(routeNormalized);
@@ -3042,7 +4488,7 @@ function getConfidence(callPath, callMethod, routePath, routeMethod) {
3042
4488
  const normalizedCall = normalizePath(stripTrailingSlash(callPath));
3043
4489
  const normalizedRoute = normalizePath(stripTrailingSlash(routePath));
3044
4490
  const exactPath = normalizedCall === normalizedRoute;
3045
- const methodMatch = callMethod === routeMethod;
4491
+ const methodMatch = callMethod === routeMethod || routeMethod === "ANY";
3046
4492
  if (exactPath && methodMatch) return "high";
3047
4493
  if (exactPath) return "medium";
3048
4494
  if (methodMatch) return "medium";
@@ -3053,11 +4499,11 @@ function detectRestApiEdges(files, projectRoot) {
3053
4499
  const allCalls = [];
3054
4500
  const allRoutes = [];
3055
4501
  for (const file of files) {
3056
- const fullPath = join9(projectRoot, file.filePath);
3057
- if (!resolve4(fullPath).startsWith(resolve4(projectRoot))) continue;
4502
+ const fullPath = join11(projectRoot, file.filePath);
4503
+ if (!resolve6(fullPath).startsWith(resolve6(projectRoot))) continue;
3058
4504
  let source;
3059
4505
  try {
3060
- source = readFileSync6(fullPath, "utf-8");
4506
+ source = readFileSync8(fullPath, "utf-8");
3061
4507
  } catch {
3062
4508
  continue;
3063
4509
  }
@@ -3093,8 +4539,8 @@ function detectRestApiEdges(files, projectRoot) {
3093
4539
  }
3094
4540
 
3095
4541
  // src/cross-language/detectors/subprocess.ts
3096
- import { readFileSync as readFileSync7 } from "fs";
3097
- import { join as join10, resolve as resolve5, basename } from "path";
4542
+ import { readFileSync as readFileSync9 } from "fs";
4543
+ import { join as join12, resolve as resolve7, basename as basename4 } from "path";
3098
4544
  var SCRIPT_EXTENSIONS = [".py", ".js", ".ts", ".go", ".rs"];
3099
4545
  function getLanguage2(filePath) {
3100
4546
  if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
@@ -3193,16 +4639,16 @@ function detectSubprocessEdges(files, projectRoot) {
3193
4639
  const knownFiles = new Set(files.map((f) => f.filePath));
3194
4640
  const basenameMap = /* @__PURE__ */ new Map();
3195
4641
  for (const f of files) {
3196
- const base = basename(f.filePath);
4642
+ const base = basename4(f.filePath);
3197
4643
  if (!basenameMap.has(base)) basenameMap.set(base, []);
3198
4644
  basenameMap.get(base).push(f.filePath);
3199
4645
  }
3200
4646
  for (const file of files) {
3201
- const fullPath = join10(projectRoot, file.filePath);
3202
- if (!resolve5(fullPath).startsWith(resolve5(projectRoot))) continue;
4647
+ const fullPath = join12(projectRoot, file.filePath);
4648
+ if (!resolve7(fullPath).startsWith(resolve7(projectRoot))) continue;
3203
4649
  let source;
3204
4650
  try {
3205
- source = readFileSync7(fullPath, "utf-8");
4651
+ source = readFileSync9(fullPath, "utf-8");
3206
4652
  } catch {
3207
4653
  continue;
3208
4654
  }
@@ -3214,7 +4660,7 @@ function detectSubprocessEdges(files, projectRoot) {
3214
4660
  targetFile = call.calledFile;
3215
4661
  confidence = "high";
3216
4662
  } else {
3217
- const base = basename(call.calledFile);
4663
+ const base = basename4(call.calledFile);
3218
4664
  const candidates = basenameMap.get(base);
3219
4665
  if (candidates && candidates.length > 0) {
3220
4666
  const exactCandidate = candidates.find((c) => c.endsWith(call.calledFile));
@@ -3593,7 +5039,7 @@ function getArchitectureSummary(graph) {
3593
5039
  }
3594
5040
 
3595
5041
  // src/health/metrics.ts
3596
- import { dirname as dirname7 } from "path";
5042
+ import { dirname as dirname9 } from "path";
3597
5043
  function scoreToGrade(score) {
3598
5044
  if (score >= 90) return "A";
3599
5045
  if (score >= 80) return "B";
@@ -3626,8 +5072,8 @@ function calculateCouplingScore(graph) {
3626
5072
  totalEdges++;
3627
5073
  fileConnections.set(sourceAttrs.filePath, (fileConnections.get(sourceAttrs.filePath) || 0) + 1);
3628
5074
  fileConnections.set(targetAttrs.filePath, (fileConnections.get(targetAttrs.filePath) || 0) + 1);
3629
- const sourceDir = dirname7(sourceAttrs.filePath).split("/")[0];
3630
- const targetDir = dirname7(targetAttrs.filePath).split("/")[0];
5075
+ const sourceDir = dirname9(sourceAttrs.filePath).split("/")[0];
5076
+ const targetDir = dirname9(targetAttrs.filePath).split("/")[0];
3631
5077
  if (sourceDir !== targetDir) {
3632
5078
  crossDirEdges++;
3633
5079
  }
@@ -3674,8 +5120,8 @@ function calculateCohesionScore(graph) {
3674
5120
  const sourceAttrs = graph.getNodeAttributes(source);
3675
5121
  const targetAttrs = graph.getNodeAttributes(target);
3676
5122
  if (sourceAttrs.filePath !== targetAttrs.filePath) {
3677
- const sourceDir = dirname7(sourceAttrs.filePath);
3678
- const targetDir = dirname7(targetAttrs.filePath);
5123
+ const sourceDir = dirname9(sourceAttrs.filePath);
5124
+ const targetDir = dirname9(targetAttrs.filePath);
3679
5125
  if (!dirEdges.has(sourceDir)) {
3680
5126
  dirEdges.set(sourceDir, { internal: 0, total: 0 });
3681
5127
  }
@@ -3957,8 +5403,8 @@ function calculateDepthScore(graph) {
3957
5403
  }
3958
5404
 
3959
5405
  // src/health/index.ts
3960
- import { readFileSync as readFileSync8, writeFileSync, existsSync as existsSync8, mkdirSync } from "fs";
3961
- import { dirname as dirname8, resolve as resolve6 } from "path";
5406
+ import { readFileSync as readFileSync10, writeFileSync, existsSync as existsSync10, mkdirSync } from "fs";
5407
+ import { dirname as dirname10, resolve as resolve8 } from "path";
3962
5408
  function calculateHealthScore(graph, projectRoot) {
3963
5409
  const coupling = calculateCouplingScore(graph);
3964
5410
  const cohesion = calculateCohesionScore(graph);
@@ -4057,8 +5503,8 @@ function getHealthTrend(projectRoot, currentScore) {
4057
5503
  }
4058
5504
  }
4059
5505
  function saveHealthHistory(projectRoot, report) {
4060
- const resolvedRoot = resolve6(projectRoot);
4061
- const historyFile = resolve6(resolvedRoot, ".depwire", "health-history.json");
5506
+ const resolvedRoot = resolve8(projectRoot);
5507
+ const historyFile = resolve8(resolvedRoot, ".depwire", "health-history.json");
4062
5508
  if (!historyFile.startsWith(resolvedRoot)) {
4063
5509
  return;
4064
5510
  }
@@ -4073,10 +5519,10 @@ function saveHealthHistory(projectRoot, report) {
4073
5519
  }))
4074
5520
  };
4075
5521
  let history = [];
4076
- if (existsSync8(historyFile)) {
5522
+ if (existsSync10(historyFile)) {
4077
5523
  try {
4078
5524
  if (!historyFile.startsWith(resolvedRoot)) return;
4079
- const content = readFileSync8(historyFile, "utf-8");
5525
+ const content = readFileSync10(historyFile, "utf-8");
4080
5526
  history = JSON.parse(content);
4081
5527
  } catch {
4082
5528
  }
@@ -4085,19 +5531,19 @@ function saveHealthHistory(projectRoot, report) {
4085
5531
  if (history.length > 50) {
4086
5532
  history = history.slice(-50);
4087
5533
  }
4088
- mkdirSync(dirname8(historyFile), { recursive: true });
5534
+ mkdirSync(dirname10(historyFile), { recursive: true });
4089
5535
  if (!historyFile.startsWith(resolvedRoot)) return;
4090
5536
  writeFileSync(historyFile, JSON.stringify(history, null, 2), "utf-8");
4091
5537
  }
4092
5538
  function loadHealthHistory(projectRoot) {
4093
- const resolvedRoot = resolve6(projectRoot);
4094
- const historyFile = resolve6(resolvedRoot, ".depwire", "health-history.json");
4095
- if (!historyFile.startsWith(resolvedRoot) || !existsSync8(historyFile)) {
5539
+ const resolvedRoot = resolve8(projectRoot);
5540
+ const historyFile = resolve8(resolvedRoot, ".depwire", "health-history.json");
5541
+ if (!historyFile.startsWith(resolvedRoot) || !existsSync10(historyFile)) {
4096
5542
  return [];
4097
5543
  }
4098
5544
  try {
4099
5545
  if (!historyFile.startsWith(resolvedRoot)) return [];
4100
- const content = readFileSync8(historyFile, "utf-8");
5546
+ const content = readFileSync10(historyFile, "utf-8");
4101
5547
  return JSON.parse(content);
4102
5548
  } catch {
4103
5549
  return [];
@@ -4106,7 +5552,7 @@ function loadHealthHistory(projectRoot) {
4106
5552
 
4107
5553
  // src/dead-code/detector.ts
4108
5554
  import path2 from "path";
4109
- import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
5555
+ import { readFileSync as readFileSync11, existsSync as existsSync11 } from "fs";
4110
5556
  function findDeadSymbols(graph, projectRoot, includeTests = false, debug = false) {
4111
5557
  const deadSymbols = [];
4112
5558
  const context = { graph, projectRoot };
@@ -4218,8 +5664,10 @@ function isRelevantForDeadCodeDetection(attrs) {
4218
5664
  "class",
4219
5665
  "interface",
4220
5666
  "type",
5667
+ "type_alias",
4221
5668
  "enum",
4222
5669
  "const",
5670
+ "constant",
4223
5671
  "let",
4224
5672
  "var",
4225
5673
  "method",
@@ -4237,11 +5685,11 @@ function getPackageEntryPoints(projectRoot) {
4237
5685
  const entryPoints = /* @__PURE__ */ new Set();
4238
5686
  const resolvedRoot = path2.resolve(projectRoot);
4239
5687
  const packageJsonPath = path2.resolve(resolvedRoot, "package.json");
4240
- if (!packageJsonPath.startsWith(resolvedRoot) || !existsSync9(packageJsonPath)) {
5688
+ if (!packageJsonPath.startsWith(resolvedRoot) || !existsSync11(packageJsonPath)) {
4241
5689
  return entryPoints;
4242
5690
  }
4243
5691
  try {
4244
- const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
5692
+ const packageJson = JSON.parse(readFileSync11(packageJsonPath, "utf-8"));
4245
5693
  if (packageJson.main) {
4246
5694
  entryPoints.add(path2.resolve(projectRoot, packageJson.main));
4247
5695
  }
@@ -4314,7 +5762,8 @@ function isTypeDeclarationFile(filePath) {
4314
5762
  return filePath.endsWith(".d.ts");
4315
5763
  }
4316
5764
  function isFrameworkAutoLoadedFile(filePath) {
4317
- return filePath.includes("/pages/") || filePath.includes("/routes/") || filePath.includes("/middleware/") || filePath.includes("/commands/") || filePath.includes("/api/") || filePath.includes("/app/");
5765
+ return filePath.includes("/pages/") || filePath.includes("/routes/") || filePath.includes("/middleware/") || filePath.includes("/commands/") || filePath.includes("/api/") || filePath.includes("/app/") || filePath.includes("/Controllers/") || filePath.includes("/Hubs/") || filePath.includes("/Migrations/") || // Java / Spring / Jakarta
5766
+ filePath.includes("/controller/") || filePath.includes("/controllers/") || filePath.includes("/service/") || filePath.includes("/repository/") || filePath.includes("/config/") || filePath.includes("/configuration/");
4318
5767
  }
4319
5768
 
4320
5769
  // src/dead-code/classifier.ts
@@ -4382,8 +5831,8 @@ function generateReason(symbol, confidence) {
4382
5831
  return "Potentially unused";
4383
5832
  }
4384
5833
  function isBarrelFile(filePath) {
4385
- const basename5 = path3.basename(filePath);
4386
- return basename5 === "index.ts" || basename5 === "index.js";
5834
+ const basename8 = path3.basename(filePath);
5835
+ return basename8 === "index.ts" || basename8 === "index.js";
4387
5836
  }
4388
5837
  function isTestFile2(filePath) {
4389
5838
  return filePath.includes("__tests__/") || filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("/test/") || filePath.includes("/tests/");
@@ -4562,11 +6011,11 @@ function filterByConfidence(symbols, minConfidence) {
4562
6011
  }
4563
6012
 
4564
6013
  // src/docs/generator.ts
4565
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync12 } from "fs";
4566
- import { join as join14 } from "path";
6014
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync14 } from "fs";
6015
+ import { join as join16 } from "path";
4567
6016
 
4568
6017
  // src/docs/architecture.ts
4569
- import { dirname as dirname9 } from "path";
6018
+ import { dirname as dirname11 } from "path";
4570
6019
 
4571
6020
  // src/docs/templates.ts
4572
6021
  function header(text, level = 1) {
@@ -4717,7 +6166,7 @@ function generateModuleStructure(graph) {
4717
6166
  function getDirectoryStats(graph) {
4718
6167
  const dirMap = /* @__PURE__ */ new Map();
4719
6168
  graph.forEachNode((node, attrs) => {
4720
- const dir = dirname9(attrs.filePath);
6169
+ const dir = dirname11(attrs.filePath);
4721
6170
  if (dir === ".") return;
4722
6171
  if (!dirMap.has(dir)) {
4723
6172
  dirMap.set(dir, {
@@ -4742,7 +6191,7 @@ function getDirectoryStats(graph) {
4742
6191
  });
4743
6192
  const filesPerDir = /* @__PURE__ */ new Map();
4744
6193
  graph.forEachNode((node, attrs) => {
4745
- const dir = dirname9(attrs.filePath);
6194
+ const dir = dirname11(attrs.filePath);
4746
6195
  if (!filesPerDir.has(dir)) {
4747
6196
  filesPerDir.set(dir, /* @__PURE__ */ new Set());
4748
6197
  }
@@ -4757,8 +6206,8 @@ function getDirectoryStats(graph) {
4757
6206
  graph.forEachEdge((edge, attrs, source, target) => {
4758
6207
  const sourceAttrs = graph.getNodeAttributes(source);
4759
6208
  const targetAttrs = graph.getNodeAttributes(target);
4760
- const sourceDir = dirname9(sourceAttrs.filePath);
4761
- const targetDir = dirname9(targetAttrs.filePath);
6209
+ const sourceDir = dirname11(sourceAttrs.filePath);
6210
+ const targetDir = dirname11(targetAttrs.filePath);
4762
6211
  if (sourceDir !== targetDir) {
4763
6212
  if (!dirEdges.has(sourceDir)) {
4764
6213
  dirEdges.set(sourceDir, { in: 0, out: 0 });
@@ -4965,7 +6414,7 @@ function detectCycles(graph) {
4965
6414
  }
4966
6415
 
4967
6416
  // src/docs/conventions.ts
4968
- import { basename as basename2, extname as extname4 } from "path";
6417
+ import { basename as basename5, extname as extname6 } from "path";
4969
6418
  function generateConventions(graph, projectRoot, version) {
4970
6419
  let output = "";
4971
6420
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -5003,7 +6452,7 @@ function generateFileOrganization(graph) {
5003
6452
  graph.forEachNode((node, attrs) => {
5004
6453
  if (!files.has(attrs.filePath)) {
5005
6454
  files.add(attrs.filePath);
5006
- const fileName = basename2(attrs.filePath);
6455
+ const fileName = basename5(attrs.filePath);
5007
6456
  if (fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx") {
5008
6457
  barrelFileCount++;
5009
6458
  }
@@ -5062,7 +6511,7 @@ function generateNamingPatterns(graph) {
5062
6511
  graph.forEachNode((node, attrs) => {
5063
6512
  if (!files.has(attrs.filePath)) {
5064
6513
  files.add(attrs.filePath);
5065
- const fileName = basename2(attrs.filePath, extname4(attrs.filePath));
6514
+ const fileName = basename5(attrs.filePath, extname6(attrs.filePath));
5066
6515
  if (isCamelCase(fileName)) patterns.files.camelCase++;
5067
6516
  else if (isPascalCase(fileName)) patterns.files.PascalCase++;
5068
6517
  else if (isKebabCase(fileName)) patterns.files.kebabCase++;
@@ -5739,7 +7188,7 @@ function detectCyclesDetailed(graph) {
5739
7188
  }
5740
7189
 
5741
7190
  // src/docs/onboarding.ts
5742
- import { dirname as dirname10 } from "path";
7191
+ import { dirname as dirname12 } from "path";
5743
7192
  function generateOnboarding(graph, projectRoot, version) {
5744
7193
  let output = "";
5745
7194
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -5798,7 +7247,7 @@ function generateQuickOrientation(graph) {
5798
7247
  const primaryLang = Object.entries(languages2).sort((a, b) => b[1] - a[1])[0];
5799
7248
  const dirs = /* @__PURE__ */ new Set();
5800
7249
  graph.forEachNode((node, attrs) => {
5801
- const dir = dirname10(attrs.filePath);
7250
+ const dir = dirname12(attrs.filePath);
5802
7251
  if (dir !== ".") {
5803
7252
  const topLevel = dir.split("/")[0];
5804
7253
  dirs.add(topLevel);
@@ -5902,7 +7351,7 @@ function generateModuleMap(graph) {
5902
7351
  function getDirectoryStats2(graph) {
5903
7352
  const dirMap = /* @__PURE__ */ new Map();
5904
7353
  graph.forEachNode((node, attrs) => {
5905
- const dir = dirname10(attrs.filePath);
7354
+ const dir = dirname12(attrs.filePath);
5906
7355
  if (dir === ".") return;
5907
7356
  if (!dirMap.has(dir)) {
5908
7357
  dirMap.set(dir, {
@@ -5917,7 +7366,7 @@ function getDirectoryStats2(graph) {
5917
7366
  });
5918
7367
  const filesPerDir = /* @__PURE__ */ new Map();
5919
7368
  graph.forEachNode((node, attrs) => {
5920
- const dir = dirname10(attrs.filePath);
7369
+ const dir = dirname12(attrs.filePath);
5921
7370
  if (!filesPerDir.has(dir)) {
5922
7371
  filesPerDir.set(dir, /* @__PURE__ */ new Set());
5923
7372
  }
@@ -5932,8 +7381,8 @@ function getDirectoryStats2(graph) {
5932
7381
  graph.forEachEdge((edge, attrs, source, target) => {
5933
7382
  const sourceAttrs = graph.getNodeAttributes(source);
5934
7383
  const targetAttrs = graph.getNodeAttributes(target);
5935
- const sourceDir = dirname10(sourceAttrs.filePath);
5936
- const targetDir = dirname10(targetAttrs.filePath);
7384
+ const sourceDir = dirname12(sourceAttrs.filePath);
7385
+ const targetDir = dirname12(targetAttrs.filePath);
5937
7386
  if (sourceDir !== targetDir) {
5938
7387
  if (!dirEdges.has(sourceDir)) {
5939
7388
  dirEdges.set(sourceDir, { in: 0, out: 0 });
@@ -6014,7 +7463,7 @@ function detectClusters(graph) {
6014
7463
  const dirFiles = /* @__PURE__ */ new Map();
6015
7464
  const fileEdges = /* @__PURE__ */ new Map();
6016
7465
  graph.forEachNode((node, attrs) => {
6017
- const dir = dirname10(attrs.filePath);
7466
+ const dir = dirname12(attrs.filePath);
6018
7467
  if (!dirFiles.has(dir)) {
6019
7468
  dirFiles.set(dir, /* @__PURE__ */ new Set());
6020
7469
  }
@@ -6068,8 +7517,8 @@ function inferClusterName(files) {
6068
7517
  if (sortedWords.length > 0 && sortedWords[0][1] > 1) {
6069
7518
  return capitalizeFirst2(sortedWords[0][0]);
6070
7519
  }
6071
- const commonDir = dirname10(files[0]);
6072
- if (files.every((f) => dirname10(f) === commonDir)) {
7520
+ const commonDir = dirname12(files[0]);
7521
+ if (files.every((f) => dirname12(f) === commonDir)) {
6073
7522
  return capitalizeFirst2(commonDir.split("/").pop() || "Core");
6074
7523
  }
6075
7524
  return "Core";
@@ -6127,7 +7576,7 @@ function generateDepwireUsage(projectRoot) {
6127
7576
  }
6128
7577
 
6129
7578
  // src/docs/files.ts
6130
- import { dirname as dirname11, basename as basename3 } from "path";
7579
+ import { dirname as dirname13, basename as basename6 } from "path";
6131
7580
  function generateFiles(graph, projectRoot, version) {
6132
7581
  let output = "";
6133
7582
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -6231,7 +7680,7 @@ function generateDirectoryBreakdown(graph) {
6231
7680
  const fileStats = getFileStats2(graph);
6232
7681
  const dirMap = /* @__PURE__ */ new Map();
6233
7682
  for (const file of fileStats) {
6234
- const dir = dirname11(file.filePath);
7683
+ const dir = dirname13(file.filePath);
6235
7684
  const topDir = dir === "." ? "." : dir.split("/")[0];
6236
7685
  if (!dirMap.has(topDir)) {
6237
7686
  dirMap.set(topDir, {
@@ -6246,7 +7695,7 @@ function generateDirectoryBreakdown(graph) {
6246
7695
  dirStats.symbolCount += file.symbolCount;
6247
7696
  if (file.totalConnections > dirStats.maxConnections) {
6248
7697
  dirStats.maxConnections = file.totalConnections;
6249
- dirStats.mostConnectedFile = basename3(file.filePath);
7698
+ dirStats.mostConnectedFile = basename6(file.filePath);
6250
7699
  }
6251
7700
  }
6252
7701
  if (dirMap.size === 0) {
@@ -6874,7 +8323,7 @@ function generateRecommendations(graph) {
6874
8323
  }
6875
8324
 
6876
8325
  // src/docs/tests.ts
6877
- import { basename as basename4, dirname as dirname12 } from "path";
8326
+ import { basename as basename7, dirname as dirname14 } from "path";
6878
8327
  function generateTests(graph, projectRoot, version) {
6879
8328
  let output = "";
6880
8329
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -6902,8 +8351,8 @@ function getFileCount8(graph) {
6902
8351
  return files.size;
6903
8352
  }
6904
8353
  function isTestFile3(filePath) {
6905
- const fileName = basename4(filePath).toLowerCase();
6906
- const dirPath = dirname12(filePath).toLowerCase();
8354
+ const fileName = basename7(filePath).toLowerCase();
8355
+ const dirPath = dirname14(filePath).toLowerCase();
6907
8356
  if (dirPath.includes("test") || dirPath.includes("spec") || dirPath.includes("__tests__")) {
6908
8357
  return true;
6909
8358
  }
@@ -6961,13 +8410,13 @@ function generateTestFileInventory(graph) {
6961
8410
  return output;
6962
8411
  }
6963
8412
  function matchTestToSource(testFile) {
6964
- const testFileName = basename4(testFile);
6965
- const testDir = dirname12(testFile);
8413
+ const testFileName = basename7(testFile);
8414
+ const testDir = dirname14(testFile);
6966
8415
  let sourceFileName = testFileName.replace(/\.test\./g, ".").replace(/\.spec\./g, ".").replace(/_test\./g, ".").replace(/_spec\./g, ".");
6967
8416
  const possiblePaths = [];
6968
8417
  possiblePaths.push(testDir + "/" + sourceFileName);
6969
8418
  if (testDir.endsWith("/test") || testDir.endsWith("/tests") || testDir.endsWith("/__tests__")) {
6970
- const parentDir = dirname12(testDir);
8419
+ const parentDir = dirname14(testDir);
6971
8420
  possiblePaths.push(parentDir + "/" + sourceFileName);
6972
8421
  }
6973
8422
  if (testDir.includes("test")) {
@@ -7123,7 +8572,7 @@ function generateTestCoverageMap(graph) {
7123
8572
  const rows = mappings.slice(0, 30).map((m) => [
7124
8573
  `\`${m.sourceFile}\``,
7125
8574
  m.hasTest ? "\u2705" : "\u274C",
7126
- m.testFile ? `\`${basename4(m.testFile)}\`` : "-",
8575
+ m.testFile ? `\`${basename7(m.testFile)}\`` : "-",
7127
8576
  formatNumber(m.symbolCount)
7128
8577
  ]);
7129
8578
  let output = table(headers, rows);
@@ -7164,7 +8613,7 @@ function generateTestStatistics(graph) {
7164
8613
  `;
7165
8614
  const dirTestCoverage = /* @__PURE__ */ new Map();
7166
8615
  for (const sourceFile of sourceFiles) {
7167
- const dir = dirname12(sourceFile).split("/")[0];
8616
+ const dir = dirname14(sourceFile).split("/")[0];
7168
8617
  if (!dirTestCoverage.has(dir)) {
7169
8618
  dirTestCoverage.set(dir, { total: 0, tested: 0 });
7170
8619
  }
@@ -7187,7 +8636,7 @@ function generateTestStatistics(graph) {
7187
8636
  }
7188
8637
 
7189
8638
  // src/docs/history.ts
7190
- import { dirname as dirname13 } from "path";
8639
+ import { dirname as dirname15 } from "path";
7191
8640
  import { execSync } from "child_process";
7192
8641
  function generateHistory(graph, projectRoot, version) {
7193
8642
  let output = "";
@@ -7468,7 +8917,7 @@ function generateFeatureClusters(graph) {
7468
8917
  const dirFiles = /* @__PURE__ */ new Map();
7469
8918
  const fileEdges = /* @__PURE__ */ new Map();
7470
8919
  graph.forEachNode((node, attrs) => {
7471
- const dir = dirname13(attrs.filePath);
8920
+ const dir = dirname15(attrs.filePath);
7472
8921
  if (!dirFiles.has(dir)) {
7473
8922
  dirFiles.set(dir, /* @__PURE__ */ new Set());
7474
8923
  }
@@ -7550,7 +8999,7 @@ function capitalizeFirst3(str) {
7550
8999
  }
7551
9000
 
7552
9001
  // src/docs/current.ts
7553
- import { dirname as dirname14 } from "path";
9002
+ import { dirname as dirname16 } from "path";
7554
9003
  function generateCurrent(graph, projectRoot, version) {
7555
9004
  let output = "";
7556
9005
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -7688,7 +9137,7 @@ function generateCompleteFileIndex(graph) {
7688
9137
  fileInfos.sort((a, b) => a.filePath.localeCompare(b.filePath));
7689
9138
  const dirGroups = /* @__PURE__ */ new Map();
7690
9139
  for (const info of fileInfos) {
7691
- const dir = dirname14(info.filePath);
9140
+ const dir = dirname16(info.filePath);
7692
9141
  const topDir = dir === "." ? "root" : dir.split("/")[0];
7693
9142
  if (!dirGroups.has(topDir)) {
7694
9143
  dirGroups.set(topDir, []);
@@ -7899,8 +9348,8 @@ function getTopLevelDir2(filePath) {
7899
9348
  }
7900
9349
 
7901
9350
  // src/docs/status.ts
7902
- import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
7903
- import { resolve as resolve7 } from "path";
9351
+ import { readFileSync as readFileSync12, existsSync as existsSync12 } from "fs";
9352
+ import { resolve as resolve9 } from "path";
7904
9353
  function generateStatus(graph, projectRoot, version) {
7905
9354
  let output = "";
7906
9355
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -7933,16 +9382,16 @@ function getFileCount11(graph) {
7933
9382
  }
7934
9383
  function extractComments(projectRoot, filePath) {
7935
9384
  const comments = [];
7936
- const resolvedRoot = resolve7(projectRoot);
7937
- const fullPath = resolve7(resolvedRoot, filePath);
9385
+ const resolvedRoot = resolve9(projectRoot);
9386
+ const fullPath = resolve9(resolvedRoot, filePath);
7938
9387
  if (!fullPath.startsWith(resolvedRoot)) {
7939
9388
  return comments;
7940
9389
  }
7941
- if (!existsSync10(fullPath)) {
9390
+ if (!existsSync12(fullPath)) {
7942
9391
  return comments;
7943
9392
  }
7944
9393
  try {
7945
- const content = readFileSync10(fullPath, "utf-8");
9394
+ const content = readFileSync12(fullPath, "utf-8");
7946
9395
  const lines = content.split("\n");
7947
9396
  const patterns = [
7948
9397
  { type: "TODO", regex: /(?:\/\/|#|\/\*)\s*TODO:?\s*(.+)/i },
@@ -8521,16 +9970,16 @@ function generateConfidenceSection(title, description, symbols, projectRoot) {
8521
9970
  }
8522
9971
 
8523
9972
  // src/docs/metadata.ts
8524
- import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync2 } from "fs";
8525
- import { resolve as resolve8 } from "path";
9973
+ import { existsSync as existsSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync2 } from "fs";
9974
+ import { resolve as resolve10 } from "path";
8526
9975
  function loadMetadata(outputDir) {
8527
- const resolvedDir = resolve8(outputDir);
8528
- const metadataPath = resolve8(resolvedDir, "metadata.json");
8529
- if (!metadataPath.startsWith(resolvedDir) || !existsSync11(metadataPath)) {
9976
+ const resolvedDir = resolve10(outputDir);
9977
+ const metadataPath = resolve10(resolvedDir, "metadata.json");
9978
+ if (!metadataPath.startsWith(resolvedDir) || !existsSync13(metadataPath)) {
8530
9979
  return null;
8531
9980
  }
8532
9981
  try {
8533
- const content = readFileSync11(metadataPath, "utf-8");
9982
+ const content = readFileSync13(metadataPath, "utf-8");
8534
9983
  return JSON.parse(content);
8535
9984
  } catch (err) {
8536
9985
  console.error("Failed to load metadata:", err);
@@ -8538,8 +9987,8 @@ function loadMetadata(outputDir) {
8538
9987
  }
8539
9988
  }
8540
9989
  function saveMetadata(outputDir, metadata) {
8541
- const resolvedDir = resolve8(outputDir);
8542
- const metadataPath = resolve8(resolvedDir, "metadata.json");
9990
+ const resolvedDir = resolve10(outputDir);
9991
+ const metadataPath = resolve10(resolvedDir, "metadata.json");
8543
9992
  if (!metadataPath.startsWith(resolvedDir)) {
8544
9993
  throw new Error(`Path traversal attempt blocked: ${metadataPath}`);
8545
9994
  }
@@ -8585,7 +10034,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8585
10034
  const generated = [];
8586
10035
  const errors = [];
8587
10036
  try {
8588
- if (!existsSync12(options.outputDir)) {
10037
+ if (!existsSync14(options.outputDir)) {
8589
10038
  mkdirSync2(options.outputDir, { recursive: true });
8590
10039
  if (options.verbose) {
8591
10040
  console.log(`Created output directory: ${options.outputDir}`);
@@ -8624,7 +10073,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8624
10073
  try {
8625
10074
  if (options.verbose) console.log("Generating ARCHITECTURE.md...");
8626
10075
  const content = generateArchitecture(graph, projectRoot, version, parseTime);
8627
- const filePath = join14(options.outputDir, "ARCHITECTURE.md");
10076
+ const filePath = join16(options.outputDir, "ARCHITECTURE.md");
8628
10077
  writeFileSync3(filePath, content, "utf-8");
8629
10078
  generated.push("ARCHITECTURE.md");
8630
10079
  } catch (err) {
@@ -8635,7 +10084,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8635
10084
  try {
8636
10085
  if (options.verbose) console.log("Generating CONVENTIONS.md...");
8637
10086
  const content = generateConventions(graph, projectRoot, version);
8638
- const filePath = join14(options.outputDir, "CONVENTIONS.md");
10087
+ const filePath = join16(options.outputDir, "CONVENTIONS.md");
8639
10088
  writeFileSync3(filePath, content, "utf-8");
8640
10089
  generated.push("CONVENTIONS.md");
8641
10090
  } catch (err) {
@@ -8646,7 +10095,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8646
10095
  try {
8647
10096
  if (options.verbose) console.log("Generating DEPENDENCIES.md...");
8648
10097
  const content = generateDependencies(graph, projectRoot, version);
8649
- const filePath = join14(options.outputDir, "DEPENDENCIES.md");
10098
+ const filePath = join16(options.outputDir, "DEPENDENCIES.md");
8650
10099
  writeFileSync3(filePath, content, "utf-8");
8651
10100
  generated.push("DEPENDENCIES.md");
8652
10101
  } catch (err) {
@@ -8657,7 +10106,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8657
10106
  try {
8658
10107
  if (options.verbose) console.log("Generating ONBOARDING.md...");
8659
10108
  const content = generateOnboarding(graph, projectRoot, version);
8660
- const filePath = join14(options.outputDir, "ONBOARDING.md");
10109
+ const filePath = join16(options.outputDir, "ONBOARDING.md");
8661
10110
  writeFileSync3(filePath, content, "utf-8");
8662
10111
  generated.push("ONBOARDING.md");
8663
10112
  } catch (err) {
@@ -8668,7 +10117,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8668
10117
  try {
8669
10118
  if (options.verbose) console.log("Generating FILES.md...");
8670
10119
  const content = generateFiles(graph, projectRoot, version);
8671
- const filePath = join14(options.outputDir, "FILES.md");
10120
+ const filePath = join16(options.outputDir, "FILES.md");
8672
10121
  writeFileSync3(filePath, content, "utf-8");
8673
10122
  generated.push("FILES.md");
8674
10123
  } catch (err) {
@@ -8679,7 +10128,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8679
10128
  try {
8680
10129
  if (options.verbose) console.log("Generating API_SURFACE.md...");
8681
10130
  const content = generateApiSurface(graph, projectRoot, version);
8682
- const filePath = join14(options.outputDir, "API_SURFACE.md");
10131
+ const filePath = join16(options.outputDir, "API_SURFACE.md");
8683
10132
  writeFileSync3(filePath, content, "utf-8");
8684
10133
  generated.push("API_SURFACE.md");
8685
10134
  } catch (err) {
@@ -8690,7 +10139,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8690
10139
  try {
8691
10140
  if (options.verbose) console.log("Generating ERRORS.md...");
8692
10141
  const content = generateErrors(graph, projectRoot, version);
8693
- const filePath = join14(options.outputDir, "ERRORS.md");
10142
+ const filePath = join16(options.outputDir, "ERRORS.md");
8694
10143
  writeFileSync3(filePath, content, "utf-8");
8695
10144
  generated.push("ERRORS.md");
8696
10145
  } catch (err) {
@@ -8701,7 +10150,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8701
10150
  try {
8702
10151
  if (options.verbose) console.log("Generating TESTS.md...");
8703
10152
  const content = generateTests(graph, projectRoot, version);
8704
- const filePath = join14(options.outputDir, "TESTS.md");
10153
+ const filePath = join16(options.outputDir, "TESTS.md");
8705
10154
  writeFileSync3(filePath, content, "utf-8");
8706
10155
  generated.push("TESTS.md");
8707
10156
  } catch (err) {
@@ -8712,7 +10161,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8712
10161
  try {
8713
10162
  if (options.verbose) console.log("Generating HISTORY.md...");
8714
10163
  const content = generateHistory(graph, projectRoot, version);
8715
- const filePath = join14(options.outputDir, "HISTORY.md");
10164
+ const filePath = join16(options.outputDir, "HISTORY.md");
8716
10165
  writeFileSync3(filePath, content, "utf-8");
8717
10166
  generated.push("HISTORY.md");
8718
10167
  } catch (err) {
@@ -8723,7 +10172,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8723
10172
  try {
8724
10173
  if (options.verbose) console.log("Generating CURRENT.md...");
8725
10174
  const content = generateCurrent(graph, projectRoot, version);
8726
- const filePath = join14(options.outputDir, "CURRENT.md");
10175
+ const filePath = join16(options.outputDir, "CURRENT.md");
8727
10176
  writeFileSync3(filePath, content, "utf-8");
8728
10177
  generated.push("CURRENT.md");
8729
10178
  } catch (err) {
@@ -8734,7 +10183,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8734
10183
  try {
8735
10184
  if (options.verbose) console.log("Generating STATUS.md...");
8736
10185
  const content = generateStatus(graph, projectRoot, version);
8737
- const filePath = join14(options.outputDir, "STATUS.md");
10186
+ const filePath = join16(options.outputDir, "STATUS.md");
8738
10187
  writeFileSync3(filePath, content, "utf-8");
8739
10188
  generated.push("STATUS.md");
8740
10189
  } catch (err) {
@@ -8745,7 +10194,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8745
10194
  try {
8746
10195
  if (options.verbose) console.log("Generating HEALTH.md...");
8747
10196
  const content = generateHealth(graph, projectRoot, version);
8748
- const filePath = join14(options.outputDir, "HEALTH.md");
10197
+ const filePath = join16(options.outputDir, "HEALTH.md");
8749
10198
  writeFileSync3(filePath, content, "utf-8");
8750
10199
  generated.push("HEALTH.md");
8751
10200
  } catch (err) {
@@ -8756,7 +10205,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8756
10205
  try {
8757
10206
  if (options.verbose) console.log("Generating DEAD_CODE.md...");
8758
10207
  const content = generateDeadCode(graph, projectRoot, version);
8759
- const filePath = join14(options.outputDir, "DEAD_CODE.md");
10208
+ const filePath = join16(options.outputDir, "DEAD_CODE.md");
8760
10209
  writeFileSync3(filePath, content, "utf-8");
8761
10210
  generated.push("DEAD_CODE.md");
8762
10211
  } catch (err) {
@@ -8800,7 +10249,7 @@ function getFileCount13(graph) {
8800
10249
  }
8801
10250
 
8802
10251
  // src/simulation/engine.ts
8803
- import { dirname as dirname15, join as join15 } from "path";
10252
+ import { dirname as dirname17, join as join17 } from "path";
8804
10253
  function normalizePath2(p) {
8805
10254
  return p.replace(/^\.\//, "").replace(/\/+$/, "");
8806
10255
  }
@@ -8931,7 +10380,7 @@ var SimulationEngine = class {
8931
10380
  }
8932
10381
  }
8933
10382
  applyRename(clone, target, newName, brokenImports) {
8934
- const destination = join15(dirname15(target), newName);
10383
+ const destination = join17(dirname17(target), newName);
8935
10384
  this.applyMove(clone, target, destination, brokenImports);
8936
10385
  }
8937
10386
  applySplit(clone, target, newFile, symbols, brokenImports) {
@@ -9131,13 +10580,13 @@ var SimulationEngine = class {
9131
10580
  };
9132
10581
 
9133
10582
  // src/security/scanner.ts
9134
- import { existsSync as existsSync14 } from "fs";
9135
- import { join as join25 } from "path";
10583
+ import { existsSync as existsSync16 } from "fs";
10584
+ import { join as join27 } from "path";
9136
10585
 
9137
10586
  // src/security/checks/dependencies.ts
9138
10587
  import { execSync as execSync2 } from "child_process";
9139
- import { existsSync as existsSync13, readFileSync as readFileSync12, readdirSync as readdirSync5 } from "fs";
9140
- import { join as join16 } from "path";
10588
+ import { existsSync as existsSync15, readFileSync as readFileSync14, readdirSync as readdirSync7 } from "fs";
10589
+ import { join as join18 } from "path";
9141
10590
  function cvssToSeverity(score) {
9142
10591
  if (score >= 9) return "critical";
9143
10592
  if (score >= 7) return "high";
@@ -9147,18 +10596,18 @@ function cvssToSeverity(score) {
9147
10596
  async function checkDependencies(_files, projectRoot) {
9148
10597
  const findings = [];
9149
10598
  try {
9150
- if (existsSync13(join16(projectRoot, "package.json"))) {
10599
+ if (existsSync15(join18(projectRoot, "package.json"))) {
9151
10600
  findings.push(...checkNpmAudit(projectRoot));
9152
10601
  findings.push(...checkPackageJsonPatterns(projectRoot));
9153
10602
  findings.push(...checkPostinstallScripts(projectRoot));
9154
10603
  }
9155
- if (existsSync13(join16(projectRoot, "requirements.txt")) || existsSync13(join16(projectRoot, "pyproject.toml"))) {
10604
+ if (existsSync15(join18(projectRoot, "requirements.txt")) || existsSync15(join18(projectRoot, "pyproject.toml"))) {
9156
10605
  findings.push(...checkPipAudit(projectRoot));
9157
10606
  }
9158
- if (existsSync13(join16(projectRoot, "Cargo.toml"))) {
10607
+ if (existsSync15(join18(projectRoot, "Cargo.toml"))) {
9159
10608
  findings.push(...checkCargoAudit(projectRoot));
9160
10609
  }
9161
- if (existsSync13(join16(projectRoot, "go.mod"))) {
10610
+ if (existsSync15(join18(projectRoot, "go.mod"))) {
9162
10611
  findings.push(...checkGoVerify(projectRoot));
9163
10612
  }
9164
10613
  } catch (err) {
@@ -9247,8 +10696,8 @@ function checkNpmAudit(projectRoot) {
9247
10696
  function checkPackageJsonPatterns(projectRoot) {
9248
10697
  const findings = [];
9249
10698
  try {
9250
- const pkgPath = join16(projectRoot, "package.json");
9251
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
10699
+ const pkgPath = join18(projectRoot, "package.json");
10700
+ const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
9252
10701
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
9253
10702
  for (const [name, version] of Object.entries(allDeps)) {
9254
10703
  if (version.startsWith("^") || version.startsWith("~")) {
@@ -9270,15 +10719,15 @@ function checkPackageJsonPatterns(projectRoot) {
9270
10719
  }
9271
10720
  function checkPostinstallScripts(projectRoot) {
9272
10721
  const findings = [];
9273
- const nodeModules = join16(projectRoot, "node_modules");
9274
- if (!existsSync13(nodeModules)) return findings;
10722
+ const nodeModules = join18(projectRoot, "node_modules");
10723
+ if (!existsSync15(nodeModules)) return findings;
9275
10724
  try {
9276
- const topLevelDeps = readdirSync5(nodeModules).filter((d) => !d.startsWith("."));
10725
+ const topLevelDeps = readdirSync7(nodeModules).filter((d) => !d.startsWith("."));
9277
10726
  for (const dep of topLevelDeps) {
9278
- const depPkgPath = join16(nodeModules, dep, "package.json");
9279
- if (!existsSync13(depPkgPath)) continue;
10727
+ const depPkgPath = join18(nodeModules, dep, "package.json");
10728
+ if (!existsSync15(depPkgPath)) continue;
9280
10729
  try {
9281
- const depPkg = JSON.parse(readFileSync12(depPkgPath, "utf-8"));
10730
+ const depPkg = JSON.parse(readFileSync14(depPkgPath, "utf-8"));
9282
10731
  const scripts = depPkg.scripts || {};
9283
10732
  if (scripts.postinstall || scripts.preinstall || scripts.install) {
9284
10733
  const scriptName = scripts.postinstall ? "postinstall" : scripts.preinstall ? "preinstall" : "install";
@@ -9316,7 +10765,7 @@ function checkPipAudit(projectRoot) {
9316
10765
  id: "",
9317
10766
  severity: cvssToSeverity(vuln.cvss?.score || 5),
9318
10767
  vulnerabilityClass: "dependency-cve",
9319
- file: existsSync13(join16(projectRoot, "requirements.txt")) ? "requirements.txt" : "pyproject.toml",
10768
+ file: existsSync15(join18(projectRoot, "requirements.txt")) ? "requirements.txt" : "pyproject.toml",
9320
10769
  title: `Vulnerable Python dependency: ${vuln.name}`,
9321
10770
  description: `${vuln.name}@${vuln.version} \u2014 ${vuln.id}: ${vuln.description || "Known vulnerability"}`,
9322
10771
  attackScenario: `An attacker could exploit the vulnerability in ${vuln.name}.`,
@@ -9413,8 +10862,8 @@ function checkGoVerify(projectRoot) {
9413
10862
  }
9414
10863
 
9415
10864
  // src/security/checks/injection.ts
9416
- import { readFileSync as readFileSync13 } from "fs";
9417
- import { join as join17 } from "path";
10865
+ import { readFileSync as readFileSync15 } from "fs";
10866
+ import { join as join19 } from "path";
9418
10867
  var SKIP_DIRS = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9419
10868
  var TEST_PATTERNS = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__"];
9420
10869
  var USER_INPUT_NAMES = /(?:input|user|name|path|query|branch|hash|cmd|command|req\.|params|body|args|url|dir|file|subdirectory)/i;
@@ -9499,6 +10948,70 @@ var PATTERNS = [
9499
10948
  description: "Database query built using fmt.Sprintf directly passed to db.Query.",
9500
10949
  attackScenario: "An attacker could inject SQL through interpolated values.",
9501
10950
  suggestedFix: 'Use parameterized queries: db.Query("SELECT ... WHERE id = ?", id)'
10951
+ },
10952
+ // Java-specific injection patterns
10953
+ {
10954
+ regex: /(?:executeQuery|executeUpdate|execute)\s*\(\s*["']?\s*(?:SELECT|INSERT|UPDATE|DELETE)\b[^"']*["']?\s*\+/i,
10955
+ title: "Java SQL injection via string concatenation",
10956
+ vulnClass: "code-injection",
10957
+ baseSeverity: "high",
10958
+ description: "SQL query built using string concatenation \u2014 vulnerable to SQL injection.",
10959
+ attackScenario: "An attacker could inject SQL through concatenated user input to read, modify, or delete database data.",
10960
+ suggestedFix: "Use PreparedStatement with parameterized queries: preparedStatement.setString(1, userInput)"
10961
+ },
10962
+ {
10963
+ regex: /Runtime\.getRuntime\(\)\.exec\s*\(/,
10964
+ title: "Java command injection via Runtime.exec",
10965
+ vulnClass: "shell-injection",
10966
+ baseSeverity: "high",
10967
+ description: "Runtime.exec() executes a system command \u2014 vulnerable if user input reaches the argument.",
10968
+ attackScenario: "An attacker could inject shell metacharacters to execute arbitrary commands on the server.",
10969
+ suggestedFix: "Use ProcessBuilder with an argument array. Validate all input against a strict allowlist."
10970
+ },
10971
+ {
10972
+ regex: /new\s+ProcessBuilder\s*\([^)]*(?:input|user|param|query|request|body|arg)/i,
10973
+ title: "Java command injection via ProcessBuilder with user input",
10974
+ vulnClass: "shell-injection",
10975
+ baseSeverity: "medium",
10976
+ description: "ProcessBuilder called with arguments that may originate from user input.",
10977
+ attackScenario: "An attacker could inject malicious arguments to the spawned process.",
10978
+ suggestedFix: "Validate all arguments against a strict allowlist before passing to ProcessBuilder."
10979
+ },
10980
+ {
10981
+ regex: /new\s+ObjectInputStream\s*\(/,
10982
+ title: "Java insecure deserialization via ObjectInputStream",
10983
+ vulnClass: "code-injection",
10984
+ baseSeverity: "high",
10985
+ description: "ObjectInputStream.readObject() deserializes arbitrary Java objects \u2014 potential RCE.",
10986
+ attackScenario: "An attacker could craft a malicious serialized object to achieve remote code execution.",
10987
+ suggestedFix: "Use a whitelist-based ObjectInputFilter, or switch to JSON/Protobuf for data exchange."
10988
+ },
10989
+ {
10990
+ regex: /DocumentBuilderFactory\.newInstance\(\)/,
10991
+ title: "Java XML External Entity (XXE) risk",
10992
+ vulnClass: "code-injection",
10993
+ baseSeverity: "medium",
10994
+ description: "DocumentBuilderFactory without FEATURE_SECURE_PROCESSING may allow XXE attacks.",
10995
+ attackScenario: "An attacker could inject external entity references in XML to read server files or perform SSRF.",
10996
+ suggestedFix: "Set FEATURE_SECURE_PROCESSING and disable external DTDs/entities: factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)"
10997
+ },
10998
+ {
10999
+ regex: /\.csrf\(\)\s*\.\s*disable\(\)/,
11000
+ title: "Spring Security CSRF protection disabled",
11001
+ vulnClass: "code-injection",
11002
+ baseSeverity: "medium",
11003
+ description: "CSRF protection has been explicitly disabled in Spring Security configuration.",
11004
+ attackScenario: "An attacker could forge cross-site requests to perform actions on behalf of authenticated users.",
11005
+ suggestedFix: "Only disable CSRF for stateless APIs using JWT. Keep CSRF enabled for session-based authentication."
11006
+ },
11007
+ {
11008
+ regex: /\.permitAll\(\).*(?:admin|manage|delete|config|setting)/i,
11009
+ title: "Spring Security permitAll on sensitive path",
11010
+ vulnClass: "code-injection",
11011
+ baseSeverity: "high",
11012
+ description: "permitAll() applied to a path that appears security-sensitive.",
11013
+ attackScenario: "An attacker could access administrative or destructive endpoints without authentication.",
11014
+ suggestedFix: 'Use .hasRole("ADMIN") or .authenticated() for sensitive endpoints.'
9502
11015
  }
9503
11016
  ];
9504
11017
  function shouldSkip(filePath) {
@@ -9515,7 +11028,7 @@ async function checkInjection(files, projectRoot) {
9515
11028
  if (shouldSkip(file.filePath) || isTestFile4(file.filePath)) continue;
9516
11029
  let content;
9517
11030
  try {
9518
- content = readFileSync13(join17(projectRoot, file.filePath), "utf-8");
11031
+ content = readFileSync15(join19(projectRoot, file.filePath), "utf-8");
9519
11032
  } catch {
9520
11033
  continue;
9521
11034
  }
@@ -9553,8 +11066,8 @@ async function checkInjection(files, projectRoot) {
9553
11066
  }
9554
11067
 
9555
11068
  // src/security/checks/secrets.ts
9556
- import { readFileSync as readFileSync14 } from "fs";
9557
- import { join as join18 } from "path";
11069
+ import { readFileSync as readFileSync16 } from "fs";
11070
+ import { join as join20 } from "path";
9558
11071
  var SKIP_DIRS2 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9559
11072
  var TEST_PATTERNS2 = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__", ".example", ".sample"];
9560
11073
  var SECRET_PATTERNS = [
@@ -9587,7 +11100,7 @@ async function checkSecrets(files, projectRoot) {
9587
11100
  if (shouldSkip2(file.filePath) || isTestFile5(file.filePath)) continue;
9588
11101
  let content;
9589
11102
  try {
9590
- content = readFileSync14(join18(projectRoot, file.filePath), "utf-8");
11103
+ content = readFileSync16(join20(projectRoot, file.filePath), "utf-8");
9591
11104
  } catch {
9592
11105
  continue;
9593
11106
  }
@@ -9620,8 +11133,8 @@ async function checkSecrets(files, projectRoot) {
9620
11133
  }
9621
11134
 
9622
11135
  // src/security/checks/path-traversal.ts
9623
- import { readFileSync as readFileSync15 } from "fs";
9624
- import { join as join19 } from "path";
11136
+ import { readFileSync as readFileSync17 } from "fs";
11137
+ import { join as join21 } from "path";
9625
11138
  var SKIP_DIRS3 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9626
11139
  var USER_INPUT_VARS = /(?:req\.|params|query|body|input|path|dir|subdirectory|file|userInput|fileName|filePath)/i;
9627
11140
  var PATTERNS2 = [
@@ -9668,7 +11181,7 @@ async function checkPathTraversal(files, projectRoot) {
9668
11181
  if (shouldSkip3(file.filePath)) continue;
9669
11182
  let content;
9670
11183
  try {
9671
- content = readFileSync15(join19(projectRoot, file.filePath), "utf-8");
11184
+ content = readFileSync17(join21(projectRoot, file.filePath), "utf-8");
9672
11185
  } catch {
9673
11186
  continue;
9674
11187
  }
@@ -9710,8 +11223,8 @@ async function checkPathTraversal(files, projectRoot) {
9710
11223
  }
9711
11224
 
9712
11225
  // src/security/checks/auth.ts
9713
- import { readFileSync as readFileSync16 } from "fs";
9714
- import { join as join20 } from "path";
11226
+ import { readFileSync as readFileSync18 } from "fs";
11227
+ import { join as join22 } from "path";
9715
11228
  var SKIP_DIRS4 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9716
11229
  function shouldSkip4(filePath) {
9717
11230
  return SKIP_DIRS4.some((d) => filePath.includes(d));
@@ -9727,7 +11240,7 @@ async function checkAuth(files, projectRoot) {
9727
11240
  if (shouldSkip4(file.filePath)) continue;
9728
11241
  let content;
9729
11242
  try {
9730
- content = readFileSync16(join20(projectRoot, file.filePath), "utf-8");
11243
+ content = readFileSync18(join22(projectRoot, file.filePath), "utf-8");
9731
11244
  } catch {
9732
11245
  continue;
9733
11246
  }
@@ -9818,8 +11331,8 @@ async function checkAuth(files, projectRoot) {
9818
11331
  }
9819
11332
 
9820
11333
  // src/security/checks/input-validation.ts
9821
- import { readFileSync as readFileSync17 } from "fs";
9822
- import { join as join21 } from "path";
11334
+ import { readFileSync as readFileSync19 } from "fs";
11335
+ import { join as join23 } from "path";
9823
11336
  var SKIP_DIRS5 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9824
11337
  function shouldSkip5(filePath) {
9825
11338
  return SKIP_DIRS5.some((d) => filePath.includes(d));
@@ -9831,7 +11344,7 @@ async function checkInputValidation(files, projectRoot) {
9831
11344
  if (shouldSkip5(file.filePath)) continue;
9832
11345
  let content;
9833
11346
  try {
9834
- content = readFileSync17(join21(projectRoot, file.filePath), "utf-8");
11347
+ content = readFileSync19(join23(projectRoot, file.filePath), "utf-8");
9835
11348
  } catch {
9836
11349
  continue;
9837
11350
  }
@@ -9908,8 +11421,8 @@ async function checkInputValidation(files, projectRoot) {
9908
11421
  }
9909
11422
 
9910
11423
  // src/security/checks/information-disclosure.ts
9911
- import { readFileSync as readFileSync18 } from "fs";
9912
- import { join as join22 } from "path";
11424
+ import { readFileSync as readFileSync20 } from "fs";
11425
+ import { join as join24 } from "path";
9913
11426
  var SKIP_DIRS6 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9914
11427
  function shouldSkip6(filePath) {
9915
11428
  return SKIP_DIRS6.some((d) => filePath.includes(d));
@@ -9921,7 +11434,7 @@ async function checkInformationDisclosure(files, projectRoot) {
9921
11434
  if (shouldSkip6(file.filePath)) continue;
9922
11435
  let content;
9923
11436
  try {
9924
- content = readFileSync18(join22(projectRoot, file.filePath), "utf-8");
11437
+ content = readFileSync20(join24(projectRoot, file.filePath), "utf-8");
9925
11438
  } catch {
9926
11439
  continue;
9927
11440
  }
@@ -9991,9 +11504,10 @@ async function checkInformationDisclosure(files, projectRoot) {
9991
11504
  }
9992
11505
 
9993
11506
  // src/security/checks/cryptography.ts
9994
- import { readFileSync as readFileSync19 } from "fs";
9995
- import { join as join23 } from "path";
11507
+ import { readFileSync as readFileSync21 } from "fs";
11508
+ import { join as join25 } from "path";
9996
11509
  var SKIP_DIRS7 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
11510
+ var USER_INPUT_NAMES2 = /(?:input|user|name|path|query|param|request|body|args|url)/i;
9997
11511
  function shouldSkip7(filePath) {
9998
11512
  return SKIP_DIRS7.some((d) => filePath.includes(d));
9999
11513
  }
@@ -10008,7 +11522,7 @@ async function checkCryptography(files, projectRoot) {
10008
11522
  if (shouldSkip7(file.filePath)) continue;
10009
11523
  let content;
10010
11524
  try {
10011
- content = readFileSync19(join23(projectRoot, file.filePath), "utf-8");
11525
+ content = readFileSync21(join25(projectRoot, file.filePath), "utf-8");
10012
11526
  } catch {
10013
11527
  continue;
10014
11528
  }
@@ -10017,7 +11531,7 @@ async function checkCryptography(files, projectRoot) {
10017
11531
  for (let i = 0; i < lines.length; i++) {
10018
11532
  const line = lines[i];
10019
11533
  if (line.trimStart().startsWith("//") || line.trimStart().startsWith("#")) continue;
10020
- if (/createHash\s*\(\s*['"]md5['"]\s*\)/.test(line) || /hashlib\.md5\s*\(/.test(line)) {
11534
+ if (/createHash\s*\(\s*['"]md5['"]\s*\)/.test(line) || /hashlib\.md5\s*\(/.test(line) || /MessageDigest\.getInstance\s*\(\s*["']MD5["']\s*\)/.test(line)) {
10021
11535
  findings.push({
10022
11536
  id: "",
10023
11537
  severity: isCryptoFile ? "high" : "medium",
@@ -10030,7 +11544,7 @@ async function checkCryptography(files, projectRoot) {
10030
11544
  suggestedFix: "Use SHA-256 or SHA-3 for integrity checks. Use bcrypt, scrypt, or argon2 for password hashing."
10031
11545
  });
10032
11546
  }
10033
- if (/createHash\s*\(\s*['"]sha1['"]\s*\)/.test(line) || /hashlib\.sha1\s*\(/.test(line)) {
11547
+ if (/createHash\s*\(\s*['"]sha1['"]\s*\)/.test(line) || /hashlib\.sha1\s*\(/.test(line) || /MessageDigest\.getInstance\s*\(\s*["']SHA-?1["']\s*\)/.test(line)) {
10034
11548
  findings.push({
10035
11549
  id: "",
10036
11550
  severity: isCryptoFile ? "high" : "medium",
@@ -10043,6 +11557,34 @@ async function checkCryptography(files, projectRoot) {
10043
11557
  suggestedFix: "Use SHA-256 or SHA-3 for integrity checks. Use bcrypt, scrypt, or argon2 for password hashing."
10044
11558
  });
10045
11559
  }
11560
+ if (/Cipher\.getInstance\s*\(\s*["']DES/.test(line)) {
11561
+ findings.push({
11562
+ id: "",
11563
+ severity: "high",
11564
+ vulnerabilityClass: "cryptography",
11565
+ file: file.filePath,
11566
+ line: i + 1,
11567
+ title: "Weak cipher algorithm: DES",
11568
+ description: "DES uses a 56-bit key and can be brute-forced in hours.",
11569
+ attackScenario: "An attacker could brute-force DES-encrypted data to reveal plaintext.",
11570
+ suggestedFix: 'Use AES-256 with GCM mode: Cipher.getInstance("AES/GCM/NoPadding")'
11571
+ });
11572
+ }
11573
+ if (/(?:log|logger|LOG)\s*\.\s*(?:info|debug|warn|error|trace)\s*\([^)]*\+/.test(line)) {
11574
+ if (USER_INPUT_NAMES2.test(line)) {
11575
+ findings.push({
11576
+ id: "",
11577
+ severity: "medium",
11578
+ vulnerabilityClass: "cryptography",
11579
+ file: file.filePath,
11580
+ line: i + 1,
11581
+ title: "Potential log injection",
11582
+ description: "User-controlled input concatenated directly into log output.",
11583
+ attackScenario: "An attacker could inject newlines or control characters to forge log entries or hide malicious activity.",
11584
+ suggestedFix: 'Use parameterized logging: log.info("User: {}", userInput) instead of string concatenation.'
11585
+ });
11586
+ }
11587
+ }
10046
11588
  if (/Math\.random\(\)/.test(line) && isCryptoFile) {
10047
11589
  findings.push({
10048
11590
  id: "",
@@ -10090,8 +11632,8 @@ async function checkCryptography(files, projectRoot) {
10090
11632
  }
10091
11633
 
10092
11634
  // src/security/checks/frontend.ts
10093
- import { readFileSync as readFileSync20 } from "fs";
10094
- import { join as join24 } from "path";
11635
+ import { readFileSync as readFileSync22 } from "fs";
11636
+ import { join as join26 } from "path";
10095
11637
  var SKIP_DIRS8 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
10096
11638
  function shouldSkip8(filePath) {
10097
11639
  return SKIP_DIRS8.some((d) => filePath.includes(d));
@@ -10107,7 +11649,7 @@ async function checkFrontend(files, projectRoot) {
10107
11649
  if (!isFrontendFile(file.filePath)) continue;
10108
11650
  let content;
10109
11651
  try {
10110
- content = readFileSync20(join24(projectRoot, file.filePath), "utf-8");
11652
+ content = readFileSync22(join26(projectRoot, file.filePath), "utf-8");
10111
11653
  } catch {
10112
11654
  continue;
10113
11655
  }
@@ -10567,11 +12109,13 @@ async function scanSecurity(projectRoot, graph, options = {}) {
10567
12109
  };
10568
12110
  }
10569
12111
  function detectPackageManager(projectRoot) {
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";
12112
+ if (existsSync16(join27(projectRoot, "package.json"))) return "npm";
12113
+ if (existsSync16(join27(projectRoot, "requirements.txt"))) return "pip";
12114
+ if (existsSync16(join27(projectRoot, "pyproject.toml"))) return "pip";
12115
+ if (existsSync16(join27(projectRoot, "Cargo.toml"))) return "cargo";
12116
+ if (existsSync16(join27(projectRoot, "go.mod"))) return "go";
12117
+ if (existsSync16(join27(projectRoot, "pom.xml"))) return "maven";
12118
+ if (existsSync16(join27(projectRoot, "build.gradle")) || existsSync16(join27(projectRoot, "build.gradle.kts"))) return "gradle";
10575
12119
  return "unknown";
10576
12120
  }
10577
12121