syncable_cli/analyzer/frameworks/
javascript.rs

1use super::{LanguageFrameworkDetector, TechnologyRule, FrameworkDetectionUtils};
2use crate::analyzer::{DetectedTechnology, DetectedLanguage, TechnologyCategory, LibraryType};
3use crate::error::Result;
4use std::path::Path;
5
6pub struct JavaScriptFrameworkDetector;
7
8impl LanguageFrameworkDetector for JavaScriptFrameworkDetector {
9    fn detect_frameworks(&self, language: &DetectedLanguage) -> Result<Vec<DetectedTechnology>> {
10        let rules = get_js_technology_rules();
11        
12        // Combine main and dev dependencies for comprehensive detection
13        let all_deps: Vec<String> = language.main_dependencies.iter()
14            .chain(language.dev_dependencies.iter())
15            .cloned()
16            .collect();
17        
18        let mut technologies = FrameworkDetectionUtils::detect_technologies_by_dependencies(
19            &rules, &all_deps, language.confidence
20        );
21        
22        // Enhanced detection: analyze actual source files for usage patterns
23        if let Some(enhanced_techs) = detect_technologies_from_source_files(language, &rules) {
24            // Merge with dependency-based detection, preferring higher confidence scores
25            for enhanced_tech in enhanced_techs {
26                if let Some(existing) = technologies.iter_mut().find(|t| t.name == enhanced_tech.name) {
27                    // Use higher confidence between dependency and source file analysis
28                    if enhanced_tech.confidence > existing.confidence {
29                        existing.confidence = enhanced_tech.confidence;
30                    }
31                } else {
32                    // Add new technology found in source files
33                    technologies.push(enhanced_tech);
34                }
35            }
36        }
37        
38        Ok(technologies)
39    }
40    
41    fn supported_languages(&self) -> Vec<&'static str> {
42        vec!["JavaScript", "TypeScript", "JavaScript/TypeScript"]
43    }
44}
45
46/// Enhanced detection that analyzes actual source files for technology usage patterns
47fn detect_technologies_from_source_files(language: &DetectedLanguage, _rules: &[TechnologyRule]) -> Option<Vec<DetectedTechnology>> {
48    use std::fs;
49    
50    let mut detected = Vec::new();
51    
52    // Analyze files for usage patterns
53    for file_path in &language.files {
54        if let Ok(content) = fs::read_to_string(file_path) {
55            // Analyze Drizzle ORM usage patterns
56            if let Some(drizzle_confidence) = analyze_drizzle_usage(&content, file_path) {
57                detected.push(DetectedTechnology {
58                    name: "Drizzle ORM".to_string(),
59                    version: None,
60                    category: TechnologyCategory::Database,
61                    confidence: drizzle_confidence,
62                    requires: vec![],
63                    conflicts_with: vec![],
64                    is_primary: false,
65                });
66            }
67            
68            // Analyze Prisma usage patterns
69            if let Some(prisma_confidence) = analyze_prisma_usage(&content, file_path) {
70                detected.push(DetectedTechnology {
71                    name: "Prisma".to_string(),
72                    version: None,
73                    category: TechnologyCategory::Database,
74                    confidence: prisma_confidence,
75                    requires: vec![],
76                    conflicts_with: vec![],
77                    is_primary: false,
78                });
79            }
80            
81            // Analyze Encore usage patterns
82            if let Some(encore_confidence) = analyze_encore_usage(&content, file_path) {
83                detected.push(DetectedTechnology {
84                    name: "Encore".to_string(),
85                    version: None,
86                    category: TechnologyCategory::BackendFramework,
87                    confidence: encore_confidence,
88                    requires: vec![],
89                    conflicts_with: vec![],
90                    is_primary: true,
91                });
92            }
93            
94            // Analyze Tanstack Start usage patterns
95            if let Some(tanstack_confidence) = analyze_tanstack_start_usage(&content, file_path) {
96                detected.push(DetectedTechnology {
97                    name: "Tanstack Start".to_string(),
98                    version: None,
99                    category: TechnologyCategory::MetaFramework,
100                    confidence: tanstack_confidence,
101                    requires: vec!["React".to_string()],
102                    conflicts_with: vec!["Next.js".to_string(), "React Router v7".to_string(), "SvelteKit".to_string(), "Nuxt.js".to_string()],
103                    is_primary: true,
104                });
105            }
106        }
107    }
108    
109    if detected.is_empty() {
110        None
111    } else {
112        Some(detected)
113    }
114}
115
116/// Analyzes Drizzle ORM usage patterns in source files
117fn analyze_drizzle_usage(content: &str, file_path: &Path) -> Option<f32> {
118    let file_name = file_path.file_name()?.to_string_lossy();
119    let mut confidence: f32 = 0.0;
120    
121    // High confidence indicators
122    if content.contains("drizzle-orm") {
123        confidence += 0.3;
124    }
125    
126    // Schema file patterns (very high confidence)
127    if file_name.contains("schema") || file_name.contains("db.ts") || file_name.contains("database") {
128        if content.contains("pgTable") || content.contains("mysqlTable") || content.contains("sqliteTable") {
129            confidence += 0.4;
130        }
131        if content.contains("pgEnum") || content.contains("relations") {
132            confidence += 0.3;
133        }
134    }
135    
136    // Drizzle-specific imports
137    if content.contains("from 'drizzle-orm/pg-core'") || 
138       content.contains("from 'drizzle-orm/mysql-core'") ||
139       content.contains("from 'drizzle-orm/sqlite-core'") {
140        confidence += 0.3;
141    }
142    
143    // Drizzle query patterns
144    if content.contains("db.select()") || content.contains("db.insert()") || 
145       content.contains("db.update()") || content.contains("db.delete()") {
146        confidence += 0.2;
147    }
148    
149    // Configuration patterns
150    if content.contains("drizzle(") && (content.contains("connectionString") || content.contains("postgres(")) {
151        confidence += 0.2;
152    }
153    
154    // Migration patterns
155    if content.contains("drizzle.config") || file_name.contains("migrate") {
156        confidence += 0.2;
157    }
158    
159    // Prepared statements
160    if content.contains(".prepare()") && content.contains("drizzle") {
161        confidence += 0.1;
162    }
163    
164    if confidence > 0.0 {
165        Some(confidence.min(1.0_f32))
166    } else {
167        None
168    }
169}
170
171/// Analyzes Prisma usage patterns in source files
172fn analyze_prisma_usage(content: &str, file_path: &Path) -> Option<f32> {
173    let file_name = file_path.file_name()?.to_string_lossy();
174    let mut confidence: f32 = 0.0;
175    let mut has_prisma_import = false;
176    
177    // Only detect Prisma if there are actual Prisma-specific imports
178    if content.contains("@prisma/client") || content.contains("from '@prisma/client'") {
179        confidence += 0.4;
180        has_prisma_import = true;
181    }
182    
183    // Prisma schema files (very specific)
184    if file_name == "schema.prisma" {
185        if content.contains("model ") || content.contains("generator ") || content.contains("datasource ") {
186            confidence += 0.6;
187            has_prisma_import = true;
188        }
189    }
190    
191    // Only check for client usage if we have confirmed Prisma imports
192    if has_prisma_import {
193        // Prisma client instantiation (very specific)
194        if content.contains("new PrismaClient") || content.contains("PrismaClient()") {
195            confidence += 0.3;
196        }
197        
198        // Prisma-specific query patterns (only if we know it's Prisma)
199        if content.contains("prisma.") && (
200            content.contains(".findUnique(") || 
201            content.contains(".findFirst(") || 
202            content.contains(".upsert(") ||
203            content.contains(".$connect()") ||
204            content.contains(".$disconnect()")
205        ) {
206            confidence += 0.2;
207        }
208    }
209    
210    // Only return confidence if we have actual Prisma indicators
211    if confidence > 0.0 && has_prisma_import {
212        Some(confidence.min(1.0_f32))
213    } else {
214        None
215    }
216}
217
218/// Analyzes Encore usage patterns in source files
219fn analyze_encore_usage(content: &str, file_path: &Path) -> Option<f32> {
220    let file_name = file_path.file_name()?.to_string_lossy();
221    let mut confidence: f32 = 0.0;
222    
223    // Skip generated files (like Encore client code)
224    if content.contains("// Code generated by the Encore") || content.contains("DO NOT EDIT") {
225        return None;
226    }
227    
228    // Skip client-only files (generated or consumption only)
229    if file_name.contains("client.ts") || file_name.contains("client.js") {
230        return None;
231    }
232    
233    // Only detect Encore when there are actual service development patterns
234    let mut has_service_patterns = false;
235    
236    // Service definition files (high confidence for actual Encore development)
237    if file_name.contains("encore.service") || file_name.contains("service.ts") {
238        confidence += 0.4;
239        has_service_patterns = true;
240    }
241    
242    // API endpoint definitions (indicates actual Encore service development)
243    if content.contains("encore.dev/api") && (content.contains("export") || content.contains("api.")) {
244        confidence += 0.4;
245        has_service_patterns = true;
246    }
247    
248    // Database service patterns (actual Encore service code)
249    if content.contains("SQLDatabase") && content.contains("encore.dev") {
250        confidence += 0.3;
251        has_service_patterns = true;
252    }
253    
254    // Secret configuration (actual Encore service code)
255    if content.contains("secret(") && content.contains("encore.dev/config") {
256        confidence += 0.3;
257        has_service_patterns = true;
258    }
259    
260    // PubSub service patterns (actual Encore service code)
261    if content.contains("Topic") && content.contains("encore.dev/pubsub") {
262        confidence += 0.3;
263        has_service_patterns = true;
264    }
265    
266    // Cron job patterns (actual Encore service code)
267    if content.contains("cron") && content.contains("encore.dev") {
268        confidence += 0.2;
269        has_service_patterns = true;
270    }
271    
272    // Only return confidence if we have actual service development patterns
273    if confidence > 0.0 && has_service_patterns {
274        Some(confidence.min(1.0_f32))
275    } else {
276        None
277    }
278}
279
280/// Analyzes Tanstack Start usage patterns in source files
281fn analyze_tanstack_start_usage(content: &str, file_path: &Path) -> Option<f32> {
282    let file_name = file_path.file_name()?.to_string_lossy();
283    let mut confidence: f32 = 0.0;
284    let mut has_start_patterns = false;
285    
286    // Configuration files (high confidence)
287    if file_name == "app.config.ts" || file_name == "app.config.js" {
288        if content.contains("@tanstack/react-start") || content.contains("tanstack") {
289            confidence += 0.5;
290            has_start_patterns = true;
291        }
292    }
293    
294    // Router configuration patterns (very high confidence)
295    if file_name.contains("router.") && (file_name.ends_with(".ts") || file_name.ends_with(".tsx")) {
296        if content.contains("createRouter") && content.contains("@tanstack/react-router") {
297            confidence += 0.4;
298            has_start_patterns = true;
299        }
300        if content.contains("routeTree") {
301            confidence += 0.2;
302            has_start_patterns = true;
303        }
304    }
305    
306    // Server entry point patterns
307    if file_name == "ssr.tsx" || file_name == "ssr.ts" {
308        if content.contains("createStartHandler") || content.contains("@tanstack/react-start/server") {
309            confidence += 0.5;
310            has_start_patterns = true;
311        }
312    }
313    
314    // Client entry point patterns
315    if file_name == "client.tsx" || file_name == "client.ts" {
316        if content.contains("StartClient") && content.contains("@tanstack/react-start") {
317            confidence += 0.5;
318            has_start_patterns = true;
319        }
320        if content.contains("hydrateRoot") && content.contains("createRouter") {
321            confidence += 0.3;
322            has_start_patterns = true;
323        }
324    }
325    
326    // Root route patterns (in app/routes/__root.tsx)
327    if file_name == "__root.tsx" || file_name == "__root.ts" {
328        if content.contains("createRootRoute") && content.contains("@tanstack/react-router") {
329            confidence += 0.4;
330            has_start_patterns = true;
331        }
332        if content.contains("HeadContent") && content.contains("Scripts") {
333            confidence += 0.3;
334            has_start_patterns = true;
335        }
336    }
337    
338    // Route files with createFileRoute
339    if file_path.to_string_lossy().contains("routes/") {
340        if content.contains("createFileRoute") && content.contains("@tanstack/react-router") {
341            confidence += 0.3;
342            has_start_patterns = true;
343        }
344    }
345    
346    // Server functions (key Tanstack Start feature)
347    if content.contains("createServerFn") && content.contains("@tanstack/react-start") {
348        confidence += 0.4;
349        has_start_patterns = true;
350    }
351    
352    // Import patterns specific to Tanstack Start
353    if content.contains("from '@tanstack/react-start'") {
354        confidence += 0.3;
355        has_start_patterns = true;
356    }
357    
358    // Vinxi configuration patterns
359    if file_name == "vinxi.config.ts" || file_name == "vinxi.config.js" {
360        confidence += 0.2;
361        has_start_patterns = true;
362    }
363    
364    // Only return confidence if we have actual Tanstack Start patterns
365    if confidence > 0.0 && has_start_patterns {
366        Some(confidence.min(1.0_f32))
367    } else {
368        None
369    }
370}
371
372/// JavaScript/TypeScript technology detection rules with proper classification
373fn get_js_technology_rules() -> Vec<TechnologyRule> {
374    vec![
375        // META-FRAMEWORKS (Mutually Exclusive)
376        TechnologyRule {
377            name: "Next.js".to_string(),
378            category: TechnologyCategory::MetaFramework,
379            confidence: 0.95,
380            dependency_patterns: vec!["next".to_string()],
381            requires: vec!["React".to_string()],
382            conflicts_with: vec!["Tanstack Start".to_string(), "React Router v7".to_string(), "SvelteKit".to_string(), "Nuxt.js".to_string()],
383            is_primary_indicator: true,
384            alternative_names: vec!["nextjs".to_string()],
385        },
386        TechnologyRule {
387            name: "Tanstack Start".to_string(),
388            category: TechnologyCategory::MetaFramework,
389            confidence: 0.95,
390            dependency_patterns: vec!["@tanstack/react-start".to_string()],
391            requires: vec!["React".to_string()],
392            conflicts_with: vec!["Next.js".to_string(), "React Router v7".to_string(), "SvelteKit".to_string(), "Nuxt.js".to_string()],
393            is_primary_indicator: true,
394            alternative_names: vec!["tanstack-start".to_string(), "TanStack Start".to_string()],
395        },
396        TechnologyRule {
397            name: "React Router v7".to_string(),
398            category: TechnologyCategory::MetaFramework,
399            confidence: 0.95,
400            dependency_patterns: vec!["react-router".to_string(), "react-dom".to_string(), "react-router-dom".to_string()],
401            requires: vec!["React".to_string()],
402            conflicts_with: vec!["Next.js".to_string(), "Tanstack Start".to_string(), "SvelteKit".to_string(), "Nuxt.js".to_string(), "React Native".to_string(), "Expo".to_string()],
403            is_primary_indicator: true,
404            alternative_names: vec!["remix".to_string(), "react-router".to_string()],
405        },
406        TechnologyRule {
407            name: "SvelteKit".to_string(),
408            category: TechnologyCategory::MetaFramework,
409            confidence: 0.95,
410            dependency_patterns: vec!["@sveltejs/kit".to_string()],
411            requires: vec!["Svelte".to_string()],
412            conflicts_with: vec!["Next.js".to_string(), "Tanstack Start".to_string(), "React Router v7".to_string(), "Nuxt.js".to_string()],
413            is_primary_indicator: true,
414            alternative_names: vec!["svelte-kit".to_string()],
415        },
416        TechnologyRule {
417            name: "Nuxt.js".to_string(),
418            category: TechnologyCategory::MetaFramework,
419            confidence: 0.95,
420            dependency_patterns: vec!["nuxt".to_string(), "@nuxt/core".to_string()],
421            requires: vec!["Vue.js".to_string()],
422            conflicts_with: vec!["Next.js".to_string(), "Tanstack Start".to_string(), "React Router v7".to_string(), "SvelteKit".to_string()],
423            is_primary_indicator: true,
424            alternative_names: vec!["nuxtjs".to_string()],
425        },
426        TechnologyRule {
427            name: "Astro".to_string(),
428            category: TechnologyCategory::MetaFramework,
429            confidence: 0.95,
430            dependency_patterns: vec!["astro".to_string()],
431            requires: vec![],
432            conflicts_with: vec![],
433            is_primary_indicator: true,
434            alternative_names: vec![],
435        },
436        TechnologyRule {
437            name: "SolidStart".to_string(),
438            category: TechnologyCategory::MetaFramework,
439            confidence: 0.95,
440            dependency_patterns: vec!["solid-start".to_string()],
441            requires: vec!["SolidJS".to_string()],
442            conflicts_with: vec!["Next.js".to_string(), "Tanstack Start".to_string(), "React Router v7".to_string(), "SvelteKit".to_string()],
443            is_primary_indicator: true,
444            alternative_names: vec![],
445        },
446        
447        // MOBILE FRAMEWORKS (React Native/Expo)
448        TechnologyRule {
449            name: "React Native".to_string(),
450            category: TechnologyCategory::FrontendFramework,
451            confidence: 0.95,
452            dependency_patterns: vec!["react-native".to_string()],
453            requires: vec!["React".to_string()],
454            conflicts_with: vec!["Next.js".to_string(), "React Router v7".to_string(), "SvelteKit".to_string(), "Nuxt.js".to_string(), "Tanstack Start".to_string()],
455            is_primary_indicator: true,
456            alternative_names: vec!["reactnative".to_string()],
457        },
458        TechnologyRule {
459            name: "Expo".to_string(),
460            category: TechnologyCategory::MetaFramework,
461            confidence: 0.98,
462            dependency_patterns: vec!["expo".to_string(), "expo-router".to_string(), "@expo/vector-icons".to_string()],
463            requires: vec!["React Native".to_string()],
464            conflicts_with: vec!["Next.js".to_string(), "React Router v7".to_string(), "SvelteKit".to_string(), "Nuxt.js".to_string(), "Tanstack Start".to_string()],
465            is_primary_indicator: true,
466            alternative_names: vec![],
467        },
468        
469        // FRONTEND FRAMEWORKS (Provide structure)
470        TechnologyRule {
471            name: "Angular".to_string(),
472            category: TechnologyCategory::FrontendFramework,
473            confidence: 0.90,
474            dependency_patterns: vec!["@angular/core".to_string()],
475            requires: vec![],
476            conflicts_with: vec![],
477            is_primary_indicator: true,
478            alternative_names: vec!["angular".to_string()],
479        },
480        TechnologyRule {
481            name: "Svelte".to_string(),
482            category: TechnologyCategory::FrontendFramework,
483            confidence: 0.95,
484            dependency_patterns: vec!["svelte".to_string()],
485            requires: vec![],
486            conflicts_with: vec![],
487            is_primary_indicator: false, // SvelteKit would be primary
488            alternative_names: vec![],
489        },
490        
491        // UI LIBRARIES (Not frameworks!)
492        TechnologyRule {
493            name: "React".to_string(),
494            category: TechnologyCategory::Library(LibraryType::UI),
495            confidence: 0.90,
496            dependency_patterns: vec!["react".to_string()],
497            requires: vec![],
498            conflicts_with: vec![],
499            is_primary_indicator: false, // Meta-frameworks using React would be primary
500            alternative_names: vec!["reactjs".to_string()],
501        },
502        TechnologyRule {
503            name: "Vue.js".to_string(),
504            category: TechnologyCategory::Library(LibraryType::UI),
505            confidence: 0.90,
506            dependency_patterns: vec!["vue".to_string()],
507            requires: vec![],
508            conflicts_with: vec![],
509            is_primary_indicator: false,
510            alternative_names: vec!["vuejs".to_string()],
511        },
512        TechnologyRule {
513            name: "SolidJS".to_string(),
514            category: TechnologyCategory::Library(LibraryType::UI),
515            confidence: 0.95,
516            dependency_patterns: vec!["solid-js".to_string()],
517            requires: vec![],
518            conflicts_with: vec![],
519            is_primary_indicator: false,
520            alternative_names: vec!["solid".to_string()],
521        },
522        TechnologyRule {
523            name: "HTMX".to_string(),
524            category: TechnologyCategory::Library(LibraryType::UI),
525            confidence: 0.95,
526            dependency_patterns: vec!["htmx.org".to_string()],
527            requires: vec![],
528            conflicts_with: vec![],
529            is_primary_indicator: false,
530            alternative_names: vec!["htmx".to_string()],
531        },
532        
533        // BACKEND FRAMEWORKS
534        TechnologyRule {
535            name: "Express.js".to_string(),
536            category: TechnologyCategory::BackendFramework,
537            confidence: 0.95,
538            dependency_patterns: vec!["express".to_string()],
539            requires: vec![],
540            conflicts_with: vec![],
541            is_primary_indicator: true,
542            alternative_names: vec!["express".to_string()],
543        },
544        TechnologyRule {
545            name: "Fastify".to_string(),
546            category: TechnologyCategory::BackendFramework,
547            confidence: 0.95,
548            dependency_patterns: vec!["fastify".to_string()],
549            requires: vec![],
550            conflicts_with: vec![],
551            is_primary_indicator: true,
552            alternative_names: vec![],
553        },
554        TechnologyRule {
555            name: "Nest.js".to_string(),
556            category: TechnologyCategory::BackendFramework,
557            confidence: 0.95,
558            dependency_patterns: vec!["@nestjs/core".to_string()],
559            requires: vec![],
560            conflicts_with: vec![],
561            is_primary_indicator: true,
562            alternative_names: vec!["nestjs".to_string()],
563        },
564        TechnologyRule {
565            name: "Hono".to_string(),
566            category: TechnologyCategory::BackendFramework,
567            confidence: 0.95,
568            dependency_patterns: vec!["hono".to_string()],
569            requires: vec![],
570            conflicts_with: vec![],
571            is_primary_indicator: true,
572            alternative_names: vec![],
573        },
574        TechnologyRule {
575            name: "Elysia".to_string(),
576            category: TechnologyCategory::BackendFramework,
577            confidence: 0.95,
578            dependency_patterns: vec!["elysia".to_string()],
579            requires: vec![],
580            conflicts_with: vec![],
581            is_primary_indicator: true,
582            alternative_names: vec![],
583        },
584        TechnologyRule {
585            name: "Encore".to_string(),
586            category: TechnologyCategory::BackendFramework,
587            confidence: 0.95,
588            dependency_patterns: vec!["encore.dev".to_string(), "encore".to_string()],
589            requires: vec![],
590            conflicts_with: vec![],
591            is_primary_indicator: true,
592            alternative_names: vec!["encore-ts-starter".to_string()],
593        },
594        
595        // BUILD TOOLS (Not frameworks!)
596        TechnologyRule {
597            name: "Vite".to_string(),
598            category: TechnologyCategory::BuildTool,
599            confidence: 0.80,
600            dependency_patterns: vec!["vite".to_string()],
601            requires: vec![],
602            conflicts_with: vec![],
603            is_primary_indicator: false,
604            alternative_names: vec![],
605        },
606        TechnologyRule {
607            name: "Webpack".to_string(),
608            category: TechnologyCategory::BuildTool,
609            confidence: 0.80,
610            dependency_patterns: vec!["webpack".to_string()],
611            requires: vec![],
612            conflicts_with: vec![],
613            is_primary_indicator: false,
614            alternative_names: vec![],
615        },
616        
617        // DATABASE/ORM (Important for Docker/infrastructure setup, migrations, etc.)
618        TechnologyRule {
619            name: "Prisma".to_string(),
620            category: TechnologyCategory::Database,
621            confidence: 0.90,
622            dependency_patterns: vec!["prisma".to_string(), "@prisma/client".to_string()],
623            requires: vec![],
624            conflicts_with: vec![],
625            is_primary_indicator: false,
626            alternative_names: vec![],
627        },
628        TechnologyRule {
629            name: "Drizzle ORM".to_string(),
630            category: TechnologyCategory::Database,
631            confidence: 0.90,
632            dependency_patterns: vec!["drizzle-orm".to_string(), "drizzle-kit".to_string()],
633            requires: vec![],
634            conflicts_with: vec![],
635            is_primary_indicator: false,
636            alternative_names: vec!["drizzle".to_string()],
637        },
638        TechnologyRule {
639            name: "Sequelize".to_string(),
640            category: TechnologyCategory::Database,
641            confidence: 0.90,
642            dependency_patterns: vec!["sequelize".to_string()],
643            requires: vec![],
644            conflicts_with: vec![],
645            is_primary_indicator: false,
646            alternative_names: vec![],
647        },
648        TechnologyRule {
649            name: "TypeORM".to_string(),
650            category: TechnologyCategory::Database,
651            confidence: 0.90,
652            dependency_patterns: vec!["typeorm".to_string()],
653            requires: vec![],
654            conflicts_with: vec![],
655            is_primary_indicator: false,
656            alternative_names: vec![],
657        },
658        TechnologyRule {
659            name: "MikroORM".to_string(),
660            category: TechnologyCategory::Database,
661            confidence: 0.90,
662            dependency_patterns: vec!["@mikro-orm/core".to_string(), "@mikro-orm/postgresql".to_string(), "@mikro-orm/mysql".to_string(), "@mikro-orm/sqlite".to_string(), "@mikro-orm/mongodb".to_string()],
663            requires: vec![],
664            conflicts_with: vec![],
665            is_primary_indicator: false,
666            alternative_names: vec!["mikro-orm".to_string()],
667        },
668        TechnologyRule {
669            name: "Mongoose".to_string(),
670            category: TechnologyCategory::Database,
671            confidence: 0.95,
672            dependency_patterns: vec!["mongoose".to_string()],
673            requires: vec![],
674            conflicts_with: vec![],
675            is_primary_indicator: false,
676            alternative_names: vec![],
677        },
678        TechnologyRule {
679            name: "Typegoose".to_string(),
680            category: TechnologyCategory::Database,
681            confidence: 0.90,
682            dependency_patterns: vec!["@typegoose/typegoose".to_string()],
683            requires: vec!["Mongoose".to_string()],
684            conflicts_with: vec![],
685            is_primary_indicator: false,
686            alternative_names: vec![],
687        },
688        TechnologyRule {
689            name: "Objection.js".to_string(),
690            category: TechnologyCategory::Database,
691            confidence: 0.90,
692            dependency_patterns: vec!["objection".to_string()],
693            requires: vec!["Knex.js".to_string()],
694            conflicts_with: vec![],
695            is_primary_indicator: false,
696            alternative_names: vec!["objectionjs".to_string()],
697        },
698        TechnologyRule {
699            name: "Bookshelf".to_string(),
700            category: TechnologyCategory::Database,
701            confidence: 0.85,
702            dependency_patterns: vec!["bookshelf".to_string()],
703            requires: vec!["Knex.js".to_string()],
704            conflicts_with: vec![],
705            is_primary_indicator: false,
706            alternative_names: vec![],
707        },
708        TechnologyRule {
709            name: "Waterline".to_string(),
710            category: TechnologyCategory::Database,
711            confidence: 0.85,
712            dependency_patterns: vec!["waterline".to_string(), "sails-mysql".to_string(), "sails-postgresql".to_string(), "sails-disk".to_string()],
713            requires: vec![],
714            conflicts_with: vec![],
715            is_primary_indicator: false,
716            alternative_names: vec![],
717        },
718        TechnologyRule {
719            name: "Knex.js".to_string(),
720            category: TechnologyCategory::Database,
721            confidence: 0.85,
722            dependency_patterns: vec!["knex".to_string()],
723            requires: vec![],
724            conflicts_with: vec![],
725            is_primary_indicator: false,
726            alternative_names: vec!["knexjs".to_string()],
727        },
728        
729        // RUNTIMES (Important for IaC - determines base images, package managers)
730        TechnologyRule {
731            name: "Node.js".to_string(),
732            category: TechnologyCategory::Runtime,
733            confidence: 0.90,
734            dependency_patterns: vec!["node".to_string()], // This will need file-based detection
735            requires: vec![],
736            conflicts_with: vec![],
737            is_primary_indicator: false,
738            alternative_names: vec!["nodejs".to_string()],
739        },
740        TechnologyRule {
741            name: "Bun".to_string(),
742            category: TechnologyCategory::Runtime,
743            confidence: 0.95,
744            dependency_patterns: vec!["bun".to_string()], // Look for bun in devDependencies or bun.lockb file
745            requires: vec![],
746            conflicts_with: vec![],
747            is_primary_indicator: false,
748            alternative_names: vec![],
749        },
750        TechnologyRule {
751            name: "Deno".to_string(),
752            category: TechnologyCategory::Runtime,
753            confidence: 0.95,
754            dependency_patterns: vec!["@deno/core".to_string(), "deno".to_string()],
755            requires: vec![],
756            conflicts_with: vec![],
757            is_primary_indicator: false,
758            alternative_names: vec![],
759        },
760        TechnologyRule {
761            name: "WinterJS".to_string(),
762            category: TechnologyCategory::Runtime,
763            confidence: 0.95,
764            dependency_patterns: vec!["winterjs".to_string(), "winter-js".to_string()],
765            requires: vec![],
766            conflicts_with: vec![],
767            is_primary_indicator: false,
768            alternative_names: vec!["winter.js".to_string()],
769        },
770        TechnologyRule {
771            name: "Cloudflare Workers".to_string(),
772            category: TechnologyCategory::Runtime,
773            confidence: 0.90,
774            dependency_patterns: vec!["@cloudflare/workers-types".to_string(), "@cloudflare/vitest-pool-workers".to_string(), "wrangler".to_string()],
775            requires: vec![],
776            conflicts_with: vec![],
777            is_primary_indicator: false,
778            alternative_names: vec!["cloudflare-workers".to_string()],
779        },
780        TechnologyRule {
781            name: "Vercel Edge Runtime".to_string(),
782            category: TechnologyCategory::Runtime,
783            confidence: 0.90,
784            dependency_patterns: vec!["@vercel/edge-runtime".to_string(), "@edge-runtime/vm".to_string()],
785            requires: vec![],
786            conflicts_with: vec![],
787            is_primary_indicator: false,
788            alternative_names: vec!["vercel-edge".to_string()],
789        },
790        TechnologyRule {
791            name: "Hermes".to_string(),
792            category: TechnologyCategory::Runtime,
793            confidence: 0.85,
794            dependency_patterns: vec!["hermes-engine".to_string()],
795            requires: vec!["React Native".to_string()],
796            conflicts_with: vec![],
797            is_primary_indicator: false,
798            alternative_names: vec![],
799        },
800        TechnologyRule {
801            name: "Electron".to_string(),
802            category: TechnologyCategory::Runtime,
803            confidence: 0.95,
804            dependency_patterns: vec!["electron".to_string()],
805            requires: vec![],
806            conflicts_with: vec![],
807            is_primary_indicator: false,
808            alternative_names: vec![],
809        },
810        TechnologyRule {
811            name: "Tauri".to_string(),
812            category: TechnologyCategory::Runtime,
813            confidence: 0.95,
814            dependency_patterns: vec!["@tauri-apps/cli".to_string(), "@tauri-apps/api".to_string()],
815            requires: vec![],
816            conflicts_with: vec!["Electron".to_string()],
817            is_primary_indicator: false,
818            alternative_names: vec![],
819        },
820        TechnologyRule {
821            name: "QuickJS".to_string(),
822            category: TechnologyCategory::Runtime,
823            confidence: 0.85,
824            dependency_patterns: vec!["quickjs".to_string(), "quickjs-emscripten".to_string()],
825            requires: vec![],
826            conflicts_with: vec![],
827            is_primary_indicator: false,
828            alternative_names: vec![],
829        },
830        
831        // TESTING (Keep minimal - only major frameworks that affect build process)
832        TechnologyRule {
833            name: "Jest".to_string(),
834            category: TechnologyCategory::Testing,
835            confidence: 0.85,
836            dependency_patterns: vec!["jest".to_string()],
837            requires: vec![],
838            conflicts_with: vec![],
839            is_primary_indicator: false,
840            alternative_names: vec![],
841        },
842        TechnologyRule {
843            name: "Vitest".to_string(),
844            category: TechnologyCategory::Testing,
845            confidence: 0.85,
846            dependency_patterns: vec!["vitest".to_string()],
847            requires: vec![],
848            conflicts_with: vec![],
849            is_primary_indicator: false,
850            alternative_names: vec![],
851        },
852    ]
853}