@codexa/cli 8.5.0 → 8.6.9

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.
@@ -1,358 +1,358 @@
1
- /**
2
- * .NET Ecosystem Detector
3
- *
4
- * Detects C#, F#, VB.NET projects including:
5
- * - ASP.NET Core (MVC, Web API, Razor, Blazor)
6
- * - .NET MAUI
7
- * - Entity Framework
8
- * - Dapper
9
- * - xUnit, NUnit, MSTest
10
- */
11
-
12
- import { join } from "path";
13
- import {
14
- registerDetector,
15
- Detector,
16
- DetectorResult,
17
- DetectedTechnology,
18
- fileExists,
19
- dirExists,
20
- findFiles,
21
- readText,
22
- } from "./index";
23
-
24
- interface CsprojInfo {
25
- path: string;
26
- sdk?: string;
27
- targetFramework?: string;
28
- packages: { name: string; version: string }[];
29
- projectReferences: string[];
30
- }
31
-
32
- function parseCsproj(content: string): Partial<CsprojInfo> {
33
- const result: Partial<CsprojInfo> = { packages: [], projectReferences: [] };
34
-
35
- // Extract SDK
36
- const sdkMatch = content.match(/<Project\s+Sdk="([^"]+)"/i);
37
- if (sdkMatch) result.sdk = sdkMatch[1];
38
-
39
- // Extract TargetFramework(s)
40
- const tfMatch = content.match(/<TargetFramework>([^<]+)<\/TargetFramework>/i);
41
- if (tfMatch) result.targetFramework = tfMatch[1];
42
-
43
- const tfsMatch = content.match(/<TargetFrameworks>([^<]+)<\/TargetFrameworks>/i);
44
- if (tfsMatch) result.targetFramework = tfsMatch[1].split(";")[0];
45
-
46
- // Extract PackageReferences
47
- const packageRegex = /<PackageReference\s+Include="([^"]+)"(?:\s+Version="([^"]+)")?/gi;
48
- let match;
49
- while ((match = packageRegex.exec(content)) !== null) {
50
- result.packages!.push({ name: match[1], version: match[2] || "unknown" });
51
- }
52
-
53
- // Extract ProjectReferences
54
- const projRefRegex = /<ProjectReference\s+Include="([^"]+)"/gi;
55
- while ((match = projRefRegex.exec(content)) !== null) {
56
- result.projectReferences!.push(match[1]);
57
- }
58
-
59
- return result;
60
- }
61
-
62
- function parseSln(content: string): string[] {
63
- const projects: string[] = [];
64
- const projectRegex = /Project\([^)]+\)\s*=\s*"[^"]+",\s*"([^"]+\.csproj)"/gi;
65
- let match;
66
- while ((match = projectRegex.exec(content)) !== null) {
67
- projects.push(match[1].replace(/\\/g, "/"));
68
- }
69
- return projects;
70
- }
71
-
72
- const dotnetDetector: Detector = {
73
- name: "dotnet",
74
- ecosystem: "dotnet",
75
- priority: 100,
76
- markers: [
77
- { type: "glob", pattern: "*.sln", weight: 1.0 },
78
- { type: "glob", pattern: "*.csproj", weight: 1.0 },
79
- { type: "glob", pattern: "**/*.csproj", weight: 0.9 },
80
- { type: "glob", pattern: "*.fsproj", weight: 1.0 },
81
- { type: "glob", pattern: "*.vbproj", weight: 1.0 },
82
- { type: "file", pattern: "global.json", weight: 0.8 },
83
- { type: "file", pattern: "nuget.config", weight: 0.6 },
84
- { type: "file", pattern: "Directory.Build.props", weight: 0.7 },
85
- { type: "directory", pattern: "obj", weight: 0.3 },
86
- { type: "directory", pattern: "bin", weight: 0.3 },
87
- ],
88
-
89
- async detect(cwd: string): Promise<DetectorResult | null> {
90
- const technologies: DetectedTechnology[] = [];
91
- const structure: Record<string, string> = {};
92
- const configFiles: string[] = [];
93
-
94
- // Find all project files
95
- const slnFiles = findFiles(cwd, "*.sln");
96
- const csprojFiles = findFiles(cwd, "**/*.csproj");
97
- const fsprojFiles = findFiles(cwd, "**/*.fsproj");
98
-
99
- if (csprojFiles.length === 0 && fsprojFiles.length === 0 && slnFiles.length === 0) {
100
- return null;
101
- }
102
-
103
- // Parse solution file if exists
104
- if (slnFiles.length > 0) {
105
- configFiles.push(slnFiles[0]);
106
- }
107
-
108
- // Track detected packages across all projects
109
- const allPackages = new Map<string, string>();
110
- const projectInfos: CsprojInfo[] = [];
111
-
112
- // Parse each csproj
113
- for (const csproj of csprojFiles) {
114
- const content = readText(join(cwd, csproj));
115
- if (!content) continue;
116
-
117
- configFiles.push(csproj);
118
- const info = parseCsproj(content);
119
- info.path = csproj;
120
- projectInfos.push(info as CsprojInfo);
121
-
122
- for (const pkg of info.packages || []) {
123
- allPackages.set(pkg.name.toLowerCase(), pkg.version);
124
- }
125
- }
126
-
127
- // Determine primary SDK/framework type
128
- const sdks = projectInfos.map(p => p.sdk).filter(Boolean);
129
- const frameworks = projectInfos.map(p => p.targetFramework).filter(Boolean);
130
-
131
- // Runtime detection
132
- if (frameworks.length > 0) {
133
- const framework = frameworks[0]!;
134
- let runtimeName = ".NET";
135
- let version = framework;
136
-
137
- if (framework.startsWith("net8")) {
138
- runtimeName = ".NET 8";
139
- version = "8.0";
140
- } else if (framework.startsWith("net7")) {
141
- runtimeName = ".NET 7";
142
- version = "7.0";
143
- } else if (framework.startsWith("net6")) {
144
- runtimeName = ".NET 6";
145
- version = "6.0";
146
- } else if (framework.startsWith("net5")) {
147
- runtimeName = ".NET 5";
148
- version = "5.0";
149
- } else if (framework.startsWith("netcoreapp")) {
150
- runtimeName = ".NET Core";
151
- version = framework.replace("netcoreapp", "");
152
- } else if (framework.startsWith("netstandard")) {
153
- runtimeName = ".NET Standard";
154
- version = framework.replace("netstandard", "");
155
- } else if (framework.startsWith("net4")) {
156
- runtimeName = ".NET Framework";
157
- version = framework.replace("net", "");
158
- }
159
-
160
- technologies.push({
161
- name: runtimeName,
162
- version,
163
- confidence: 1.0,
164
- source: projectInfos[0]?.path || "csproj",
165
- category: "runtime",
166
- metadata: { targetFramework: framework },
167
- });
168
- }
169
-
170
- // Backend framework detection
171
- const backendFrameworks = [
172
- { packages: ["microsoft.aspnetcore.app", "microsoft.aspnetcore"], name: "ASP.NET Core", category: "backend" as const },
173
- { packages: ["microsoft.aspnetcore.mvc"], name: "ASP.NET Core MVC", category: "backend" as const },
174
- { packages: ["microsoft.aspnetcore.components"], name: "Blazor", category: "frontend" as const },
175
- { packages: ["microsoft.maui"], name: ".NET MAUI", category: "frontend" as const },
176
- { packages: ["avalonia", "avalonia.desktop"], name: "Avalonia", category: "frontend" as const },
177
- { packages: ["uno.ui"], name: "Uno Platform", category: "frontend" as const },
178
- ];
179
-
180
- // Check SDK for implicit framework
181
- for (const sdk of sdks) {
182
- if (sdk?.includes("Microsoft.NET.Sdk.Web")) {
183
- technologies.push({
184
- name: "ASP.NET Core",
185
- confidence: 1.0,
186
- source: "SDK",
187
- category: "backend",
188
- });
189
- } else if (sdk?.includes("Microsoft.NET.Sdk.BlazorWebAssembly")) {
190
- technologies.push({
191
- name: "Blazor WebAssembly",
192
- confidence: 1.0,
193
- source: "SDK",
194
- category: "frontend",
195
- });
196
- } else if (sdk?.includes("Microsoft.NET.Sdk.Razor")) {
197
- technologies.push({
198
- name: "Razor",
199
- confidence: 0.9,
200
- source: "SDK",
201
- category: "frontend",
202
- });
203
- }
204
- }
205
-
206
- for (const fw of backendFrameworks) {
207
- const found = fw.packages.find(p => allPackages.has(p));
208
- if (found) {
209
- technologies.push({
210
- name: fw.name,
211
- version: allPackages.get(found),
212
- confidence: 0.95,
213
- source: `Package: ${found}`,
214
- category: fw.category,
215
- });
216
- }
217
- }
218
-
219
- // ORM detection
220
- const ormMap = [
221
- { packages: ["microsoft.entityframeworkcore", "entityframework"], name: "Entity Framework", category: "orm" as const },
222
- { packages: ["dapper"], name: "Dapper", category: "orm" as const },
223
- { packages: ["npgsql.entityframeworkcore.postgresql"], name: "EF Core + PostgreSQL", category: "orm" as const },
224
- { packages: ["pomelo.entityframeworkcore.mysql"], name: "EF Core + MySQL", category: "orm" as const },
225
- { packages: ["microsoft.entityframeworkcore.sqlserver"], name: "EF Core + SQL Server", category: "orm" as const },
226
- { packages: ["microsoft.entityframeworkcore.sqlite"], name: "EF Core + SQLite", category: "orm" as const },
227
- { packages: ["nhibernate"], name: "NHibernate", category: "orm" as const },
228
- ];
229
-
230
- for (const orm of ormMap) {
231
- const found = orm.packages.find(p => allPackages.has(p));
232
- if (found) {
233
- technologies.push({
234
- name: orm.name,
235
- version: allPackages.get(found),
236
- confidence: 0.95,
237
- source: `Package: ${found}`,
238
- category: orm.category,
239
- });
240
- }
241
- }
242
-
243
- // Database detection
244
- const dbMap = [
245
- { packages: ["npgsql"], name: "PostgreSQL", category: "database" as const },
246
- { packages: ["mysql.data", "mysqlconnector"], name: "MySQL", category: "database" as const },
247
- { packages: ["microsoft.data.sqlclient", "system.data.sqlclient"], name: "SQL Server", category: "database" as const },
248
- { packages: ["microsoft.data.sqlite"], name: "SQLite", category: "database" as const },
249
- { packages: ["mongodb.driver"], name: "MongoDB", category: "database" as const },
250
- { packages: ["stackexchange.redis"], name: "Redis", category: "database" as const },
251
- ];
252
-
253
- for (const db of dbMap) {
254
- const found = db.packages.find(p => allPackages.has(p));
255
- if (found) {
256
- technologies.push({
257
- name: db.name,
258
- version: allPackages.get(found),
259
- confidence: 0.9,
260
- source: `Package: ${found}`,
261
- category: db.category,
262
- });
263
- }
264
- }
265
-
266
- // Testing framework detection
267
- const testMap = [
268
- { packages: ["xunit", "xunit.runner.visualstudio"], name: "xUnit", category: "testing" as const },
269
- { packages: ["nunit", "nunit3testadapter"], name: "NUnit", category: "testing" as const },
270
- { packages: ["mstest.testframework", "microsoft.net.test.sdk"], name: "MSTest", category: "testing" as const },
271
- { packages: ["moq"], name: "Moq", category: "testing" as const },
272
- { packages: ["nsubstitute"], name: "NSubstitute", category: "testing" as const },
273
- { packages: ["fluentassertions"], name: "FluentAssertions", category: "testing" as const },
274
- { packages: ["specflow"], name: "SpecFlow", category: "testing" as const },
275
- ];
276
-
277
- for (const test of testMap) {
278
- const found = test.packages.find(p => allPackages.has(p));
279
- if (found) {
280
- technologies.push({
281
- name: test.name,
282
- version: allPackages.get(found),
283
- confidence: 0.9,
284
- source: `Package: ${found}`,
285
- category: test.category,
286
- });
287
- }
288
- }
289
-
290
- // Auth detection
291
- const authMap = [
292
- { packages: ["microsoft.aspnetcore.authentication.jwtbearer"], name: "JWT Bearer", category: "auth" as const },
293
- { packages: ["microsoft.aspnetcore.identity"], name: "ASP.NET Identity", category: "auth" as const },
294
- { packages: ["identityserver4"], name: "IdentityServer4", category: "auth" as const },
295
- { packages: ["duende.identityserver"], name: "Duende IdentityServer", category: "auth" as const },
296
- { packages: ["microsoft.identity.web"], name: "Microsoft Identity Web", category: "auth" as const },
297
- ];
298
-
299
- for (const auth of authMap) {
300
- const found = auth.packages.find(p => allPackages.has(p));
301
- if (found) {
302
- technologies.push({
303
- name: auth.name,
304
- version: allPackages.get(found),
305
- confidence: 0.9,
306
- source: `Package: ${found}`,
307
- category: auth.category,
308
- });
309
- }
310
- }
311
-
312
- // Structure detection
313
- const structurePaths = [
314
- { key: "controllers", paths: ["Controllers", "Api/Controllers", "src/Controllers"] },
315
- { key: "services", paths: ["Services", "src/Services", "Application/Services"] },
316
- { key: "models", paths: ["Models", "Domain/Models", "Entities", "Domain/Entities"] },
317
- { key: "data", paths: ["Data", "Infrastructure/Data", "Persistence"] },
318
- { key: "api", paths: ["Api", "WebApi", "src/Api"] },
319
- { key: "tests", paths: ["Tests", "test", "tests", "*.Tests"] },
320
- { key: "migrations", paths: ["Migrations", "Data/Migrations"] },
321
- ];
322
-
323
- for (const { key, paths } of structurePaths) {
324
- for (const p of paths) {
325
- if (p.includes("*")) {
326
- const matches = findFiles(cwd, p);
327
- if (matches.length > 0) {
328
- structure[key] = matches[0];
329
- break;
330
- }
331
- } else if (dirExists(join(cwd, p))) {
332
- structure[key] = p;
333
- break;
334
- }
335
- }
336
- }
337
-
338
- // Check for config files
339
- const dotnetConfigs = ["appsettings.json", "appsettings.Development.json", "launchSettings.json", "web.config"];
340
- for (const cfg of dotnetConfigs) {
341
- if (fileExists(join(cwd, cfg)) || findFiles(cwd, `**/${cfg}`).length > 0) {
342
- configFiles.push(cfg);
343
- }
344
- }
345
-
346
- return {
347
- ecosystem: "dotnet",
348
- technologies,
349
- structure,
350
- configFiles: [...new Set(configFiles)],
351
- };
352
- },
353
- };
354
-
355
- // Register the detector
356
- registerDetector(dotnetDetector);
357
-
1
+ /**
2
+ * .NET Ecosystem Detector
3
+ *
4
+ * Detects C#, F#, VB.NET projects including:
5
+ * - ASP.NET Core (MVC, Web API, Razor, Blazor)
6
+ * - .NET MAUI
7
+ * - Entity Framework
8
+ * - Dapper
9
+ * - xUnit, NUnit, MSTest
10
+ */
11
+
12
+ import { join } from "path";
13
+ import {
14
+ registerDetector,
15
+ Detector,
16
+ DetectorResult,
17
+ DetectedTechnology,
18
+ fileExists,
19
+ dirExists,
20
+ findFiles,
21
+ readText,
22
+ } from "./index";
23
+
24
+ interface CsprojInfo {
25
+ path: string;
26
+ sdk?: string;
27
+ targetFramework?: string;
28
+ packages: { name: string; version: string }[];
29
+ projectReferences: string[];
30
+ }
31
+
32
+ function parseCsproj(content: string): Partial<CsprojInfo> {
33
+ const result: Partial<CsprojInfo> = { packages: [], projectReferences: [] };
34
+
35
+ // Extract SDK
36
+ const sdkMatch = content.match(/<Project\s+Sdk="([^"]+)"/i);
37
+ if (sdkMatch) result.sdk = sdkMatch[1];
38
+
39
+ // Extract TargetFramework(s)
40
+ const tfMatch = content.match(/<TargetFramework>([^<]+)<\/TargetFramework>/i);
41
+ if (tfMatch) result.targetFramework = tfMatch[1];
42
+
43
+ const tfsMatch = content.match(/<TargetFrameworks>([^<]+)<\/TargetFrameworks>/i);
44
+ if (tfsMatch) result.targetFramework = tfsMatch[1].split(";")[0];
45
+
46
+ // Extract PackageReferences
47
+ const packageRegex = /<PackageReference\s+Include="([^"]+)"(?:\s+Version="([^"]+)")?/gi;
48
+ let match;
49
+ while ((match = packageRegex.exec(content)) !== null) {
50
+ result.packages!.push({ name: match[1], version: match[2] || "unknown" });
51
+ }
52
+
53
+ // Extract ProjectReferences
54
+ const projRefRegex = /<ProjectReference\s+Include="([^"]+)"/gi;
55
+ while ((match = projRefRegex.exec(content)) !== null) {
56
+ result.projectReferences!.push(match[1]);
57
+ }
58
+
59
+ return result;
60
+ }
61
+
62
+ function parseSln(content: string): string[] {
63
+ const projects: string[] = [];
64
+ const projectRegex = /Project\([^)]+\)\s*=\s*"[^"]+",\s*"([^"]+\.csproj)"/gi;
65
+ let match;
66
+ while ((match = projectRegex.exec(content)) !== null) {
67
+ projects.push(match[1].replace(/\\/g, "/"));
68
+ }
69
+ return projects;
70
+ }
71
+
72
+ const dotnetDetector: Detector = {
73
+ name: "dotnet",
74
+ ecosystem: "dotnet",
75
+ priority: 100,
76
+ markers: [
77
+ { type: "glob", pattern: "*.sln", weight: 1.0 },
78
+ { type: "glob", pattern: "*.csproj", weight: 1.0 },
79
+ { type: "glob", pattern: "**/*.csproj", weight: 0.9 },
80
+ { type: "glob", pattern: "*.fsproj", weight: 1.0 },
81
+ { type: "glob", pattern: "*.vbproj", weight: 1.0 },
82
+ { type: "file", pattern: "global.json", weight: 0.8 },
83
+ { type: "file", pattern: "nuget.config", weight: 0.6 },
84
+ { type: "file", pattern: "Directory.Build.props", weight: 0.7 },
85
+ { type: "directory", pattern: "obj", weight: 0.3 },
86
+ { type: "directory", pattern: "bin", weight: 0.3 },
87
+ ],
88
+
89
+ async detect(cwd: string): Promise<DetectorResult | null> {
90
+ const technologies: DetectedTechnology[] = [];
91
+ const structure: Record<string, string> = {};
92
+ const configFiles: string[] = [];
93
+
94
+ // Find all project files
95
+ const slnFiles = findFiles(cwd, "*.sln");
96
+ const csprojFiles = findFiles(cwd, "**/*.csproj");
97
+ const fsprojFiles = findFiles(cwd, "**/*.fsproj");
98
+
99
+ if (csprojFiles.length === 0 && fsprojFiles.length === 0 && slnFiles.length === 0) {
100
+ return null;
101
+ }
102
+
103
+ // Parse solution file if exists
104
+ if (slnFiles.length > 0) {
105
+ configFiles.push(slnFiles[0]);
106
+ }
107
+
108
+ // Track detected packages across all projects
109
+ const allPackages = new Map<string, string>();
110
+ const projectInfos: CsprojInfo[] = [];
111
+
112
+ // Parse each csproj
113
+ for (const csproj of csprojFiles) {
114
+ const content = readText(join(cwd, csproj));
115
+ if (!content) continue;
116
+
117
+ configFiles.push(csproj);
118
+ const info = parseCsproj(content);
119
+ info.path = csproj;
120
+ projectInfos.push(info as CsprojInfo);
121
+
122
+ for (const pkg of info.packages || []) {
123
+ allPackages.set(pkg.name.toLowerCase(), pkg.version);
124
+ }
125
+ }
126
+
127
+ // Determine primary SDK/framework type
128
+ const sdks = projectInfos.map(p => p.sdk).filter(Boolean);
129
+ const frameworks = projectInfos.map(p => p.targetFramework).filter(Boolean);
130
+
131
+ // Runtime detection
132
+ if (frameworks.length > 0) {
133
+ const framework = frameworks[0]!;
134
+ let runtimeName = ".NET";
135
+ let version = framework;
136
+
137
+ if (framework.startsWith("net8")) {
138
+ runtimeName = ".NET 8";
139
+ version = "8.0";
140
+ } else if (framework.startsWith("net7")) {
141
+ runtimeName = ".NET 7";
142
+ version = "7.0";
143
+ } else if (framework.startsWith("net6")) {
144
+ runtimeName = ".NET 6";
145
+ version = "6.0";
146
+ } else if (framework.startsWith("net5")) {
147
+ runtimeName = ".NET 5";
148
+ version = "5.0";
149
+ } else if (framework.startsWith("netcoreapp")) {
150
+ runtimeName = ".NET Core";
151
+ version = framework.replace("netcoreapp", "");
152
+ } else if (framework.startsWith("netstandard")) {
153
+ runtimeName = ".NET Standard";
154
+ version = framework.replace("netstandard", "");
155
+ } else if (framework.startsWith("net4")) {
156
+ runtimeName = ".NET Framework";
157
+ version = framework.replace("net", "");
158
+ }
159
+
160
+ technologies.push({
161
+ name: runtimeName,
162
+ version,
163
+ confidence: 1.0,
164
+ source: projectInfos[0]?.path || "csproj",
165
+ category: "runtime",
166
+ metadata: { targetFramework: framework },
167
+ });
168
+ }
169
+
170
+ // Backend framework detection
171
+ const backendFrameworks = [
172
+ { packages: ["microsoft.aspnetcore.app", "microsoft.aspnetcore"], name: "ASP.NET Core", category: "backend" as const },
173
+ { packages: ["microsoft.aspnetcore.mvc"], name: "ASP.NET Core MVC", category: "backend" as const },
174
+ { packages: ["microsoft.aspnetcore.components"], name: "Blazor", category: "frontend" as const },
175
+ { packages: ["microsoft.maui"], name: ".NET MAUI", category: "frontend" as const },
176
+ { packages: ["avalonia", "avalonia.desktop"], name: "Avalonia", category: "frontend" as const },
177
+ { packages: ["uno.ui"], name: "Uno Platform", category: "frontend" as const },
178
+ ];
179
+
180
+ // Check SDK for implicit framework
181
+ for (const sdk of sdks) {
182
+ if (sdk?.includes("Microsoft.NET.Sdk.Web")) {
183
+ technologies.push({
184
+ name: "ASP.NET Core",
185
+ confidence: 1.0,
186
+ source: "SDK",
187
+ category: "backend",
188
+ });
189
+ } else if (sdk?.includes("Microsoft.NET.Sdk.BlazorWebAssembly")) {
190
+ technologies.push({
191
+ name: "Blazor WebAssembly",
192
+ confidence: 1.0,
193
+ source: "SDK",
194
+ category: "frontend",
195
+ });
196
+ } else if (sdk?.includes("Microsoft.NET.Sdk.Razor")) {
197
+ technologies.push({
198
+ name: "Razor",
199
+ confidence: 0.9,
200
+ source: "SDK",
201
+ category: "frontend",
202
+ });
203
+ }
204
+ }
205
+
206
+ for (const fw of backendFrameworks) {
207
+ const found = fw.packages.find(p => allPackages.has(p));
208
+ if (found) {
209
+ technologies.push({
210
+ name: fw.name,
211
+ version: allPackages.get(found),
212
+ confidence: 0.95,
213
+ source: `Package: ${found}`,
214
+ category: fw.category,
215
+ });
216
+ }
217
+ }
218
+
219
+ // ORM detection
220
+ const ormMap = [
221
+ { packages: ["microsoft.entityframeworkcore", "entityframework"], name: "Entity Framework", category: "orm" as const },
222
+ { packages: ["dapper"], name: "Dapper", category: "orm" as const },
223
+ { packages: ["npgsql.entityframeworkcore.postgresql"], name: "EF Core + PostgreSQL", category: "orm" as const },
224
+ { packages: ["pomelo.entityframeworkcore.mysql"], name: "EF Core + MySQL", category: "orm" as const },
225
+ { packages: ["microsoft.entityframeworkcore.sqlserver"], name: "EF Core + SQL Server", category: "orm" as const },
226
+ { packages: ["microsoft.entityframeworkcore.sqlite"], name: "EF Core + SQLite", category: "orm" as const },
227
+ { packages: ["nhibernate"], name: "NHibernate", category: "orm" as const },
228
+ ];
229
+
230
+ for (const orm of ormMap) {
231
+ const found = orm.packages.find(p => allPackages.has(p));
232
+ if (found) {
233
+ technologies.push({
234
+ name: orm.name,
235
+ version: allPackages.get(found),
236
+ confidence: 0.95,
237
+ source: `Package: ${found}`,
238
+ category: orm.category,
239
+ });
240
+ }
241
+ }
242
+
243
+ // Database detection
244
+ const dbMap = [
245
+ { packages: ["npgsql"], name: "PostgreSQL", category: "database" as const },
246
+ { packages: ["mysql.data", "mysqlconnector"], name: "MySQL", category: "database" as const },
247
+ { packages: ["microsoft.data.sqlclient", "system.data.sqlclient"], name: "SQL Server", category: "database" as const },
248
+ { packages: ["microsoft.data.sqlite"], name: "SQLite", category: "database" as const },
249
+ { packages: ["mongodb.driver"], name: "MongoDB", category: "database" as const },
250
+ { packages: ["stackexchange.redis"], name: "Redis", category: "database" as const },
251
+ ];
252
+
253
+ for (const db of dbMap) {
254
+ const found = db.packages.find(p => allPackages.has(p));
255
+ if (found) {
256
+ technologies.push({
257
+ name: db.name,
258
+ version: allPackages.get(found),
259
+ confidence: 0.9,
260
+ source: `Package: ${found}`,
261
+ category: db.category,
262
+ });
263
+ }
264
+ }
265
+
266
+ // Testing framework detection
267
+ const testMap = [
268
+ { packages: ["xunit", "xunit.runner.visualstudio"], name: "xUnit", category: "testing" as const },
269
+ { packages: ["nunit", "nunit3testadapter"], name: "NUnit", category: "testing" as const },
270
+ { packages: ["mstest.testframework", "microsoft.net.test.sdk"], name: "MSTest", category: "testing" as const },
271
+ { packages: ["moq"], name: "Moq", category: "testing" as const },
272
+ { packages: ["nsubstitute"], name: "NSubstitute", category: "testing" as const },
273
+ { packages: ["fluentassertions"], name: "FluentAssertions", category: "testing" as const },
274
+ { packages: ["specflow"], name: "SpecFlow", category: "testing" as const },
275
+ ];
276
+
277
+ for (const test of testMap) {
278
+ const found = test.packages.find(p => allPackages.has(p));
279
+ if (found) {
280
+ technologies.push({
281
+ name: test.name,
282
+ version: allPackages.get(found),
283
+ confidence: 0.9,
284
+ source: `Package: ${found}`,
285
+ category: test.category,
286
+ });
287
+ }
288
+ }
289
+
290
+ // Auth detection
291
+ const authMap = [
292
+ { packages: ["microsoft.aspnetcore.authentication.jwtbearer"], name: "JWT Bearer", category: "auth" as const },
293
+ { packages: ["microsoft.aspnetcore.identity"], name: "ASP.NET Identity", category: "auth" as const },
294
+ { packages: ["identityserver4"], name: "IdentityServer4", category: "auth" as const },
295
+ { packages: ["duende.identityserver"], name: "Duende IdentityServer", category: "auth" as const },
296
+ { packages: ["microsoft.identity.web"], name: "Microsoft Identity Web", category: "auth" as const },
297
+ ];
298
+
299
+ for (const auth of authMap) {
300
+ const found = auth.packages.find(p => allPackages.has(p));
301
+ if (found) {
302
+ technologies.push({
303
+ name: auth.name,
304
+ version: allPackages.get(found),
305
+ confidence: 0.9,
306
+ source: `Package: ${found}`,
307
+ category: auth.category,
308
+ });
309
+ }
310
+ }
311
+
312
+ // Structure detection
313
+ const structurePaths = [
314
+ { key: "controllers", paths: ["Controllers", "Api/Controllers", "src/Controllers"] },
315
+ { key: "services", paths: ["Services", "src/Services", "Application/Services"] },
316
+ { key: "models", paths: ["Models", "Domain/Models", "Entities", "Domain/Entities"] },
317
+ { key: "data", paths: ["Data", "Infrastructure/Data", "Persistence"] },
318
+ { key: "api", paths: ["Api", "WebApi", "src/Api"] },
319
+ { key: "tests", paths: ["Tests", "test", "tests", "*.Tests"] },
320
+ { key: "migrations", paths: ["Migrations", "Data/Migrations"] },
321
+ ];
322
+
323
+ for (const { key, paths } of structurePaths) {
324
+ for (const p of paths) {
325
+ if (p.includes("*")) {
326
+ const matches = findFiles(cwd, p);
327
+ if (matches.length > 0) {
328
+ structure[key] = matches[0];
329
+ break;
330
+ }
331
+ } else if (dirExists(join(cwd, p))) {
332
+ structure[key] = p;
333
+ break;
334
+ }
335
+ }
336
+ }
337
+
338
+ // Check for config files
339
+ const dotnetConfigs = ["appsettings.json", "appsettings.Development.json", "launchSettings.json", "web.config"];
340
+ for (const cfg of dotnetConfigs) {
341
+ if (fileExists(join(cwd, cfg)) || findFiles(cwd, `**/${cfg}`).length > 0) {
342
+ configFiles.push(cfg);
343
+ }
344
+ }
345
+
346
+ return {
347
+ ecosystem: "dotnet",
348
+ technologies,
349
+ structure,
350
+ configFiles: [...new Set(configFiles)],
351
+ };
352
+ },
353
+ };
354
+
355
+ // Register the detector
356
+ registerDetector(dotnetDetector);
357
+
358
358
  export default dotnetDetector;