humanbehavior-js
Version:
SDK for HumanBehavior session and event recording
238 lines (209 loc) • 7.82 kB
text/typescript
/**
* Manual Framework Installation Wizard
*
* This wizard allows users to manually specify their framework instead of auto-detection.
* Useful when auto-detection fails or users want more control.
*/
import * as fs from 'fs';
import * as path from 'path';
import { AutoInstallationWizard, FrameworkInfo, CodeModification, InstallationResult } from '../core/install-wizard';
import { RemoteAIService } from '../services/remote-ai-service';
export interface ManualInstallationResult extends InstallationResult {
selectedFramework: string;
manualMode: boolean;
}
export class ManualFrameworkInstallationWizard extends AutoInstallationWizard {
private selectedFramework: string;
constructor(apiKey: string, projectRoot: string = process.cwd(), framework: string) {
super(apiKey, projectRoot);
this.selectedFramework = framework.toLowerCase();
this.framework = this.createFrameworkInfo(this.selectedFramework);
}
/**
* Manual installation with user-specified framework
*/
async install(): Promise<ManualInstallationResult> {
try {
// Step 1: Handle framework selection
if (this.selectedFramework === 'auto') {
// Use full AI detection for "Other" option
this.framework = await this.runFullDetection();
} else {
// Set framework based on user selection
this.framework = this.createFrameworkInfo(this.selectedFramework);
if (!this.framework) {
this.framework = { name: 'unknown', type: 'vanilla' };
}
// Step 2: Run full detection logic to find entry points, file names, etc.
const detectedFramework = await this.runFullDetection();
// Step 3: Merge manual framework with detected details
this.framework = {
...detectedFramework,
name: this.framework.name,
type: this.framework.type
};
}
// Step 4: Install package
await this.installPackage();
// Step 5: Generate and apply code modifications
const modifications = await this.generateModifications();
await this.applyModifications(modifications);
// Step 6: Generate next steps
const nextSteps = this.generateManualNextSteps();
return {
success: true,
framework: this.framework,
modifications,
errors: [],
nextSteps,
selectedFramework: this.selectedFramework,
manualMode: true
};
} catch (error) {
return {
success: false,
framework: this.framework || { name: 'unknown', type: 'vanilla' },
modifications: [],
errors: [error instanceof Error ? error.message : 'Unknown error'],
nextSteps: [],
selectedFramework: this.selectedFramework,
manualMode: true
};
}
}
/**
* Run full detection logic to find entry points, file names, bundler, etc.
*/
private async runFullDetection(): Promise<FrameworkInfo> {
if (this.selectedFramework === 'auto') {
// Use AI service for auto-detection
const aiService = new RemoteAIService({
apiEndpoint: 'https://ik3zxh4790.execute-api.us-east-1.amazonaws.com/prod'
});
// Use AI service directly for detection
const projectFiles = await this.scanProjectFiles();
const codeSamples = await this.extractCodeSamples(projectFiles);
const aiAnalysis = await aiService.analyzeCodePatterns(codeSamples);
return aiAnalysis.framework;
} else {
// Use traditional detection for manual frameworks
const tempWizard = new AutoInstallationWizard(this.apiKey, this.projectRoot);
const detected = await tempWizard.detectFramework();
return detected;
}
}
/**
* Scan project files for analysis
*/
private async scanProjectFiles(): Promise<string[]> {
const files: string[] = [];
const scanDir = (dir: string, depth: number = 0) => {
if (depth > 3) return; // Limit depth
try {
const items = fs.readdirSync(dir);
for (const item of items) {
const fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules') {
scanDir(fullPath, depth + 1);
} else if (stat.isFile() && this.isRelevantFile(item)) {
files.push(fullPath);
}
}
} catch (error) {
// Skip inaccessible directories
}
};
scanDir(this.projectRoot);
return files;
}
/**
* Check if file is relevant for analysis
*/
private isRelevantFile(filename: string): boolean {
const relevantExtensions = [
'.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.html',
'.json', '.config.js', '.config.ts', '.babelrc', '.eslintrc'
];
const relevantNames = [
'package.json', 'tsconfig.json', 'vite.config', 'webpack.config',
'next.config', 'nuxt.config', 'angular.json', 'svelte.config'
];
return relevantExtensions.some(ext => filename.endsWith(ext)) ||
relevantNames.some(name => filename.includes(name));
}
/**
* Extract code samples for AI analysis
*/
private async extractCodeSamples(files: string[]): Promise<string[]> {
const samples: string[] = [];
for (const file of files.slice(0, 20)) { // Limit to 20 files
try {
const content = fs.readFileSync(file, 'utf8');
const relativePath = path.relative(this.projectRoot, file);
samples.push(`File: ${relativePath}\n${content.substring(0, 1000)}`);
} catch (error) {
// Skip unreadable files
}
}
return samples;
}
/**
* Create framework info based on user selection
*/
private createFrameworkInfo(framework: string): FrameworkInfo {
const frameworkMap: Record<string, FrameworkInfo> = {
'react': { name: 'react', type: 'react' },
'nextjs': { name: 'nextjs', type: 'nextjs' },
'next': { name: 'nextjs', type: 'nextjs' },
'vue': { name: 'vue', type: 'vue' },
'nuxt': { name: 'nuxt', type: 'nuxt' },
'nuxtjs': { name: 'nuxt', type: 'nuxt' },
'angular': { name: 'angular', type: 'angular' },
'svelte': { name: 'svelte', type: 'svelte' },
'sveltekit': { name: 'svelte', type: 'svelte' },
'remix': { name: 'remix', type: 'remix' },
'astro': { name: 'astro', type: 'astro' },
'gatsby': { name: 'gatsby', type: 'gatsby' },
'vanilla': { name: 'vanilla', type: 'vanilla' },
'node': { name: 'node', type: 'node' },
'auto': { name: 'auto-detected', type: 'auto' }
};
return frameworkMap[framework] || { name: framework, type: 'vanilla' };
}
/**
* Override framework detection to use manual selection
*/
public async detectFramework(): Promise<FrameworkInfo> {
return this.framework || { name: 'unknown', type: 'vanilla' };
}
/**
* Generate next steps with manual mode info
*/
private generateManualNextSteps(): string[] {
return [
'✅ Manual framework installation completed!',
`🎯 Selected framework: ${this.framework?.name || 'unknown'}`,
`🔧 Integration strategy: ${this.getIntegrationStrategy()}`,
'🚀 Your app is now ready to track user behavior',
'📊 View sessions in your HumanBehavior dashboard'
];
}
/**
* Get integration strategy based on framework
*/
private getIntegrationStrategy(): string {
if (!this.framework?.type) return 'script';
switch (this.framework.type) {
case 'react':
case 'nextjs':
return 'provider';
case 'vue':
return 'plugin';
case 'angular':
return 'module';
default:
return 'script';
}
}
}