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
Markdown
# š SCA-Tool - Static Code Analyzer for API Cost Analysis
[](https://badge.fury.io/js/sca-tool)
[](https://opensource.org/licenses/MIT)
[](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
```