UNPKG

sca-tool

Version:

Universal static code analysis tool for calculating API operation costs across multiple frameworks (NestJS, Express, Fastify)

656 lines (519 loc) • 16.8 kB
# šŸ” SCA-Tool - Static Code Analyzer for API Cost Analysis [![npm version](https://badge.fury.io/js/sca-tool.svg)](https://badge.fury.io/js/sca-tool) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) **Analyze and optimize your API operation costs in TypeScript/JavaScript applications** SCA-Tool is a powerful static code analyzer that helps you understand and optimize the cost of API operations in your codebase. It detects database queries, HTTP requests, and other expensive operations, providing detailed cost analysis and optimization recommendations. ## ✨ Features - šŸ” **Automatic Detection** - Finds database operations, API calls, and expensive operations - šŸ“Š **Cost Analysis** - Calculates execution paths and operation costs - šŸŽÆ **Framework Support** - Built-in support for NestJS, Express, Fastify, Prisma, TypeORM, Mongoose, Supabase - šŸ“ˆ **Optimization Insights** - Identifies high-cost operations and suggests improvements - šŸ”„ **Real-time Monitoring** - Watch mode for development - šŸ“‹ **Multiple Output Formats** - Text, JSON, Markdown, HTML reports - āš™ļø **Configurable** - Customizable patterns and cost calculations - šŸ“ **Editor Integration** - Built-in support for VS Code, Cursor, WebStorm, and more ## šŸŽÆ Smart Analysis Engine SCA-Tool uses an **await-focused analysis approach** that treats complete async operations as single units: ```typescript // āœ… Analyzed as 1 operation (cost: 1) const user = await this.supabase .from('users') .select('id') .eq('email', email) .single(); // āœ… Parallel operations detected (cost: max of operations, not sum) const [user, settings, permissions] = await Promise.all([ this.supabase.from('users').select('*').eq('id', userId), this.supabase.from('settings').select('*').eq('user_id', userId), this.supabase.from('permissions').select('*').eq('user_id', userId) ]); ``` **Key Benefits:** - **Statement-level analysis** - Focuses on complete `await` statements - **Method chain recognition** - Treats `.from().select().eq()` as single operation - **Parallel detection** - Correctly identifies `Promise.all` patterns - **Accurate cost calculation** - No duplicate counting of chained methods ## šŸš€ Quick Start ### Installation ```bash # Using Bun (recommended) bun add sca-tool # Using npm npm install sca-tool # Global installation bun install -g sca-tool # or npm install -g sca-tool ``` ### Basic Usage ```bash # Initialize configuration for your project sca init --framework nestjs # Analyze a single file sca file ./src/users/users.service.ts # Analyze entire project sca project ./src # NestJS-specific analysis sca nestjs ./src # Watch for changes during development sca watch ./src/auth/auth.service.ts ``` ### šŸ“ Editor Integration SCA-Tool includes built-in editor integration with automatic detection: ```bash # Detect available editors sca editor --detect # Generate VS Code tasks for seamless integration sca editor --setup-vscode # Open files directly in your editor sca editor --open src/users.service.ts --line 25 ``` **Supported Editors:** - Visual Studio Code (`code`) - **Default** - VS Code Insiders (`code-insiders`) - Cursor (`cursor`) - WebStorm (`webstorm`) - IntelliJ IDEA (`idea`) - Sublime Text (`subl`) - Atom (`atom`) - Vim (`vim`) - Neovim (`nvim`) - Emacs (`emacs`) **VS Code Integration Features:** - Automatic task generation for project analysis - File-level analysis with keyboard shortcuts - HTML report generation - Line-specific file opening ## šŸ“– Detailed Usage ### Single File Analysis ```bash # Basic file analysis sca file ./src/users/users.service.ts --framework nestjs # With verbose output sca file ./src/users/users.service.ts --framework nestjs --verbose # Save to file sca file ./src/users/users.service.ts --output analysis.json --format json ``` ### Project Analysis ```bash # Analyze entire project sca project ./src # Specify custom patterns sca project ./src --patterns "**/*.service.ts,**/*.controller.ts" # Generate HTML report sca project ./src --format html --output report.html # JSON output for CI/CD integration sca project ./src --format json --output analysis.json ``` ### Framework-Specific Analysis ```bash # NestJS projects (analyzes services, controllers, gateways, resolvers) sca nestjs ./src # Express projects (analyzes routes and controllers) sca express ./src # Fastify projects sca fastify ./src ``` ## šŸŽÆ Supported Frameworks & ORMs ### **Database ORMs** - **Prisma**: `prisma.user.findMany()`, `prisma.user.create()` - **TypeORM**: `repository.find()`, `repository.save()` - **Mongoose**: `User.findById()`, `User.create()` ### **Backend Frameworks** - **NestJS**: Services, Controllers, Gateways, Resolvers - **Express**: Routes, Controllers, Middleware - **Fastify**: Routes, Plugins, Hooks ### **Database Services** - **Supabase**: `supabase.from().select()`, `supabase.auth.signIn()` - **Firebase**: Firestore operations - **Custom APIs**: HTTP clients (axios, fetch) ### **Caching** - **Redis**: `redis.get()`, `redis.set()` - **Memcached**: Cache operations ## šŸ“Š Example Output ```typescript // Detects parallel operations automatically const [user, profile, permissions] = await Promise.all([ prisma.user.findUnique({ where: { id } }), // Cost: 1 prisma.profile.findUnique({ where: { userId: id } }), // Cost: 1 prisma.permission.findMany({ where: { userId: id } }) // Cost: 1 ]); // Total parallel cost: 1 (max of group, not sum) // vs Sequential (would be cost: 3) const user = await prisma.user.findUnique({ where: { id } }); const profile = await prisma.profile.findUnique({ where: { userId: id } }); const permissions = await prisma.permission.findMany({ where: { userId: id } }); ``` ### Sample Analysis Report ``` ================================================================================ šŸ” API COST ANALYSIS REPORT ================================================================================ šŸ“„ File: auth.service.ts šŸ“… Generated: 2025-01-05 šŸ“Š FILE SUMMARY ---------------------------------------- Methods Analyzed: 6 Total Execution Paths: 34 Cost Range: 1 - 14 requests Average Cost: 3.24 requests šŸ“‹ AuthService.login Total Execution Paths: 3 Cost Range: 2 - 4 requests Average Cost: 3 requests 🌳 EXECUTION PATHS: 1. Remember Me Token Valid šŸ’° Cost: 3 šŸ“Š Operations: • validateRememberMeToken (supabase): 2 requests • signInWithPassword (supabase_auth): 1 request 2. Remember Me Token Expired šŸ’° Cost: 4 šŸ“Š Operations: • validateRememberMeToken (supabase): 2 requests • signInWithPassword (supabase_auth): 1 request • generateOTP (supabase): 1 request 3. No Remember Me Token šŸ’° Cost: 2 šŸ“Š Operations: • signInWithPassword (supabase_auth): 1 request • generateOTP (supabase): 1 request šŸŽÆ RECOMMENDATIONS: āœ… Cost structure looks optimal šŸ’” Consider caching remember me tokens ``` ## āš™ļø Configuration ### **Initialize Configuration** ```bash # Generate configuration for your framework sca init --framework nestjs # This creates sca-tool.config.json with optimized settings ``` ### **Configuration File Structure** ```json { "operations": { "select": 1, "insert": 1, "update": 1, "delete": 1, "signInWithPassword": 1, "signUp": 1, "from": 1, "eq": 0, "single": 0 }, "patterns": { "supabase": { "pattern": "(SupabaseService\\.supabase|supabase)\\.(from\\(|auth\\.|storage\\.)", "description": "Supabase operations" }, "supabase_query": { "pattern": "\\.(select|insert|update|upsert|delete)\\(", "description": "Supabase query operations" }, "custom_service": { "pattern": "(await )?this\\.(\\w+Service|\\w+Repository)\\.", "description": "Custom service method calls" } }, "parallelExecutionPatterns": [ "Promise.all", "Promise.allSettled", "Promise.race" ], "ignorePatterns": [ "console.log", "console.error", "Math.", "Date." ] } ``` ### **Custom Patterns** Add custom patterns for your specific operations: ```json { "patterns": { "custom_database": { "pattern": "myDB\\.(query|execute|find)", "description": "Custom database operations" }, "external_api": { "pattern": "apiClient\\.(get|post|put|delete)", "description": "External API calls" }, "cache_operations": { "pattern": "cache\\.(get|set|del)", "description": "Cache operations" } }, "operations": { "query": 1, "execute": 2, "find": 1, "get": 2, "post": 2, "put": 2, "delete": 2 } } ``` ## šŸ”§ CLI Commands Reference ### **Core Commands** ```bash # File Analysis sca file <filePath> [options] --framework <framework> Framework preset (nestjs, express, fastify) --output <file> Output file path --format <format> Output format (text, json, markdown, html) --config <file> Custom configuration file --verbose Verbose logging # Project Analysis sca project <projectPath> [options] --framework <framework> Framework preset --patterns <patterns> File patterns (comma-separated) --output <file> Output file path --format <format> Output format --config <file> Custom configuration file # Framework-Specific Analysis sca nestjs <path> [options] # NestJS projects sca express <path> [options] # Express projects sca fastify <path> [options] # Fastify projects # Configuration Management sca init [options] # Generate configuration --framework <framework> Framework preset sca config # Show current configuration # Development Tools sca watch <path> [options] # Watch for file changes sca demo # Run demo analysis ``` ### **Output Formats** ```bash # Text output (default) sca file ./src/auth.service.ts # JSON output for CI/CD sca project ./src --format json --output analysis.json # HTML report for sharing sca project ./src --format html --output report.html # Markdown for documentation sca project ./src --format markdown --output ANALYSIS.md ``` ## šŸ”„ Development Workflow ### **Development Mode** ```bash # Watch files during development sca watch ./src/users/users.service.ts --framework nestjs # Outputs real-time cost analysis as you code šŸ‘€ Watching ./src/users/users.service.ts for changes... šŸ”„ File changed, re-analyzing... āœ… Method: createUser - Cost: 2-3 requests (2 paths) āš ļø Method: updateUser - Cost: 1-5 requests (3 paths) - High variance detected ``` ### **CI/CD Integration** ```yaml # GitHub Actions example name: API Cost Analysis on: [push, pull_request] jobs: analyze: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: oven-sh/setup-bun@v1 - name: Install dependencies run: bun install - name: Run API cost analysis run: | bunx sca project ./src --format json --output analysis.json - name: Upload analysis results uses: actions/upload-artifact@v3 with: name: api-cost-analysis path: analysis.json ``` ### **IDE Integration** Add to VS Code tasks (`.vscode/tasks.json`): ```json { "version": "2.0.0", "tasks": [ { "label": "Analyze API Costs", "type": "shell", "command": "sca", "args": ["file", "${file}", "--framework", "nestjs"], "group": "build", "presentation": { "echo": true, "reveal": "always" } } ] } ``` ## šŸ“ˆ Optimization Strategies ### **Recommended Patterns** 1. **Use Parallel Execution** ```typescript // āŒ Sequential (Cost: 3) const user = await getUser(id); const posts = await getPosts(id); const comments = await getComments(id); // āœ… Parallel (Cost: 1) const [user, posts, comments] = await Promise.all([ getUser(id), getPosts(id), getComments(id) ]); ``` 2. **Reduce Database Roundtrips** ```typescript // āŒ Multiple queries (Cost: N+1) const users = await prisma.user.findMany(); for (const user of users) { user.profile = await prisma.profile.findUnique({ where: { userId: user.id } }); } // āœ… Single query with relations (Cost: 1) const users = await prisma.user.findMany({ include: { profile: true } }); ``` 3. **Implement Caching** ```typescript // Add caching layer for expensive operations @Cache('user-profile', 300) // 5 minutes async getUserProfile(id: string) { return await prisma.user.findUnique({ where: { id }, include: { profile: true } }); } ``` 4. **Use Batch Operations** ```typescript // āŒ Multiple inserts (Cost: N) for (const userData of users) { await prisma.user.create({ data: userData }); } // āœ… Batch insert (Cost: 1) await prisma.user.createMany({ data: users }); ``` ## šŸ› ļø Troubleshooting ### **Common Issues** 1. **"No operations found"** - Check if your patterns match your code - Verify framework preset is correct - Add custom patterns for non-standard operations 2. **"File not found" error** - Use absolute paths or correct relative paths - Ensure TypeScript files are accessible 3. **Incorrect costs** - Update operation costs in configuration - Add custom patterns for your specific operations ### **Custom Pattern Examples** ```json { "patterns": { "custom_database": { "pattern": "myDB\\.(query|execute|find)", "defaultCost": 1, "description": "Custom database operations" }, "external_api": { "pattern": "apiClient\\.(get|post|put|delete)", "defaultCost": 2, "description": "External API calls" }, "cache_operations": { "pattern": "cache\\.(get|set|del)", "defaultCost": 0.5, "description": "Cache operations" } } } ``` ## šŸ“š API Reference ### **Core Classes** #### `ApiCostAnalyzer` ```typescript class ApiCostAnalyzer { constructor(config?: Partial<AnalyzerConfig>) setFramework(framework: string): void updateConfig(config: Partial<AnalyzerConfig>): void analyzeFile(filePath: string): FileAnalysis analyzeFiles(filePaths: string[]): FileAnalysis[] analyzeProject(projectPath: string, patterns?: string[]): ProjectAnalysis } ``` #### `ReportGenerator` ```typescript class ReportGenerator { static generateFileReport(analysis: FileAnalysis, format: ReportFormat): string static generateProjectReport(files: FileAnalysis[], format: ReportFormat): string static saveReport(content: string, filePath: string): void } ``` ### **Configuration Types** ```typescript interface AnalyzerConfig { operations: Record<string, number>; patterns: Record<string, PatternConfig>; parallelExecutionPatterns: string[]; ignorePatterns: string[]; costMultipliers: CostMultipliers; methodCosts?: Record<string, MethodCostConfig>; } interface FileAnalysis { filePath: string; fileName: string; methods: MethodAnalysis[]; summary: FileSummary; } interface MethodAnalysis { methodName: string; className?: string; totalPaths: number; paths: ExecutionPath[]; summary: MethodSummary; recommendations: string[]; } ``` ## šŸ¤ Contributing ### **Development Setup** ```bash git clone https://github.com/mtalhazulf/sca-tool cd sca-tool bun install bun run build bun test ``` ### **Adding Framework Support** 1. Add framework preset in `src/config.ts` 2. Define operation patterns and costs 3. Add tests for the new framework 4. Update documentation ### **Contributing Guidelines** - Follow TypeScript best practices - Add tests for new features - Update documentation - Follow semantic versioning ## šŸ“„ License MIT License - see [LICENSE](LICENSE) file for details. ## šŸ™ Acknowledgments - Built with TypeScript Compiler API - Inspired by static analysis tools like ESLint and SonarQube - Designed for modern JavaScript/TypeScript frameworks ## šŸ“ž Support - šŸ› **Issues**: [GitHub Issues](https://github.com/mtalhazulf/sca-tool/issues) - šŸ“– **Documentation**: [GitHub Wiki](https://github.com/mtalhazulf/sca-tool/wiki) - šŸ’¬ **Discussions**: [GitHub Discussions](https://github.com/mtalhazulf/sca-tool/discussions) --- **Made with ā¤ļø for the TypeScript/JavaScript community** Start optimizing your API costs today: ```bash # Using Bun (recommended) bun install -g sca-tool sca init --framework nestjs sca project ./src # Or using npm npm install -g sca-tool ```