@sun-asterisk/sunlint 1.3.2 โ 1.3.3
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.
- package/CHANGELOG.md +38 -0
- package/README.md +5 -3
- package/config/rules/enhanced-rules-registry.json +144 -33
- package/core/analysis-orchestrator.js +167 -42
- package/core/auto-performance-manager.js +243 -0
- package/core/cli-action-handler.js +9 -1
- package/core/cli-program.js +19 -5
- package/core/constants/defaults.js +56 -0
- package/core/performance-optimizer.js +271 -0
- package/docs/FILE_LIMITS_COMPLETION_REPORT.md +151 -0
- package/docs/FILE_LIMITS_EXPLANATION.md +190 -0
- package/docs/PERFORMANCE.md +311 -0
- package/docs/PERFORMANCE_MIGRATION_GUIDE.md +368 -0
- package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +255 -0
- package/docs/QUICK_FILE_LIMITS.md +64 -0
- package/docs/SIMPLIFIED_USAGE_GUIDE.md +208 -0
- package/engines/heuristic-engine.js +182 -5
- package/package.json +2 -1
- package/rules/common/C048_no_bypass_architectural_layers/analyzer.js +180 -0
- package/rules/common/C048_no_bypass_architectural_layers/config.json +50 -0
- package/rules/common/C048_no_bypass_architectural_layers/symbol-based-analyzer.js +235 -0
- package/rules/common/C052_parsing_or_data_transformation/analyzer.js +180 -0
- package/rules/common/C052_parsing_or_data_transformation/config.json +50 -0
- package/rules/common/C052_parsing_or_data_transformation/symbol-based-analyzer.js +132 -0
- package/rules/index.js +2 -0
- package/rules/security/S017_use_parameterized_queries/README.md +128 -0
- package/rules/security/S017_use_parameterized_queries/analyzer.js +286 -0
- package/rules/security/S017_use_parameterized_queries/config.json +109 -0
- package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +541 -0
- package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +777 -0
- package/rules/security/S031_secure_session_cookies/README.md +127 -0
- package/rules/security/S031_secure_session_cookies/analyzer.js +245 -0
- package/rules/security/S031_secure_session_cookies/config.json +86 -0
- package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +196 -0
- package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +1084 -0
- package/rules/security/S032_httponly_session_cookies/FRAMEWORK_SUPPORT.md +209 -0
- package/rules/security/S032_httponly_session_cookies/README.md +184 -0
- package/rules/security/S032_httponly_session_cookies/analyzer.js +282 -0
- package/rules/security/S032_httponly_session_cookies/config.json +96 -0
- package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +715 -0
- package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +1348 -0
- package/rules/security/S033_samesite_session_cookies/README.md +227 -0
- package/rules/security/S033_samesite_session_cookies/analyzer.js +242 -0
- package/rules/security/S033_samesite_session_cookies/config.json +87 -0
- package/rules/security/S033_samesite_session_cookies/regex-based-analyzer.js +703 -0
- package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +732 -0
- package/rules/security/S034_host_prefix_session_cookies/README.md +204 -0
- package/rules/security/S034_host_prefix_session_cookies/analyzer.js +290 -0
- package/rules/security/S034_host_prefix_session_cookies/config.json +62 -0
- package/rules/security/S034_host_prefix_session_cookies/regex-based-analyzer.js +478 -0
- package/rules/security/S034_host_prefix_session_cookies/symbol-based-analyzer.js +277 -0
- package/rules/security/S035_path_session_cookies/README.md +257 -0
- package/rules/security/S035_path_session_cookies/analyzer.js +316 -0
- package/rules/security/S035_path_session_cookies/config.json +99 -0
- package/rules/security/S035_path_session_cookies/regex-based-analyzer.js +724 -0
- package/rules/security/S035_path_session_cookies/symbol-based-analyzer.js +373 -0
- package/scripts/batch-processing-demo.js +334 -0
- package/scripts/performance-test.js +541 -0
- package/scripts/quick-performance-test.js +108 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
## ๐ **v1.3.3 - Performance & File Limits Optimization (September 4, 2025)**
|
|
6
|
+
|
|
7
|
+
**Release Date**: September 4, 2025
|
|
8
|
+
**Type**: Performance Enhancement & User Experience
|
|
9
|
+
|
|
10
|
+
### โก **Performance Engineering**
|
|
11
|
+
- **ENHANCED**: Heuristic Engine v4.0 with integrated performance optimizations
|
|
12
|
+
- **Smart file limits**: Auto-detection prevents memory issues
|
|
13
|
+
- **Batch processing**: Optimized rule execution for large projects
|
|
14
|
+
- **Memory management**: Symbol table limits for TypeScript projects
|
|
15
|
+
- **Timeout protection**: Graceful handling of long-running analysis
|
|
16
|
+
|
|
17
|
+
### ๐๏ธ **CLI Enhancement & Clarity**
|
|
18
|
+
- **CLARIFIED**: File limit options with comprehensive documentation
|
|
19
|
+
- **`--max-files`**: Controls total analysis workload (performance)
|
|
20
|
+
- **`--max-semantic-files`**: Controls TypeScript symbol table memory
|
|
21
|
+
- **Auto-detection**: Smart defaults for 90% of use cases
|
|
22
|
+
- **Manual tuning**: Fine control for enterprise projects
|
|
23
|
+
|
|
24
|
+
### ๐ **Documentation Expansion**
|
|
25
|
+
- **NEW**: [FILE_LIMITS_EXPLANATION.md](./docs/FILE_LIMITS_EXPLANATION.md) - Comprehensive guide (5.7KB)
|
|
26
|
+
- **NEW**: [QUICK_FILE_LIMITS.md](./docs/QUICK_FILE_LIMITS.md) - Quick reference (1.8KB)
|
|
27
|
+
- **ENHANCED**: CLI help with clear usage examples
|
|
28
|
+
- **INTEGRATED**: Performance docs in README.md
|
|
29
|
+
|
|
30
|
+
### ๐ง **Architecture Improvements**
|
|
31
|
+
- **INTEGRATED**: Performance logic into heuristic engine (no separate files)
|
|
32
|
+
- **ENHANCED**: Auto-performance-manager for intelligent limit calculation
|
|
33
|
+
- **OPTIMIZED**: Memory usage patterns for large codebases
|
|
34
|
+
- **TESTED**: GitHub Actions compatibility with resource constraints
|
|
35
|
+
|
|
36
|
+
### ๐ฏ **User Experience**
|
|
37
|
+
- **90/10 Rule**: Auto-detection works for most cases, manual tuning available
|
|
38
|
+
- **Progressive disclosure**: Quick ref โ detailed guide โ implementation details
|
|
39
|
+
- **CI/CD Ready**: Optimized for memory-constrained environments
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
5
43
|
## ๐ **v1.3.2 - Precision Engineering & Rule Maturity (August 21, 2025)**
|
|
6
44
|
|
|
7
45
|
**Release Date**: August 21, 2025
|
package/README.md
CHANGED
|
@@ -9,12 +9,13 @@ Sun Lint is a universal coding standards checker providing comprehensive code qu
|
|
|
9
9
|
### **โจ Key Features**
|
|
10
10
|
- โ
**256+ Coding Rules**: Quality (161), Security (70), Performance (25)
|
|
11
11
|
- โ
**Unified Architecture**: Same adapter pattern for CLI and VSCode extension
|
|
12
|
-
- โ
**Multi-Engine Support**: Heuristic (244 rules) + ESLint (17 rules) + AI (256 rules)
|
|
12
|
+
- โ
**Multi-Engine Support**: Heuristic v4.0 (244 rules) + ESLint (17 rules) + AI (256 rules)
|
|
13
|
+
- โ
**Performance Optimized**: Auto file limits, memory management, GitHub Actions ready
|
|
13
14
|
- โ
**Built-in AST Analysis**: JavaScript/TypeScript parsing out of the box
|
|
14
15
|
- โ
**Git Integration**: `--changed-files`, `--staged-files`, `--pr-mode`
|
|
15
|
-
- โ
**TypeScript Support**: Native TypeScript 5.8+ analysis
|
|
16
|
+
- โ
**TypeScript Support**: Native TypeScript 5.8+ analysis with smart memory limits
|
|
16
17
|
- โ
**Zero Config**: Works immediately after `npm install`
|
|
17
|
-
- โ
**CI/CD Ready**: Baseline comparison, fail-on-new-violations
|
|
18
|
+
- โ
**CI/CD Ready**: Baseline comparison, fail-on-new-violations, timeout protection
|
|
18
19
|
- โ
**Advanced File Targeting**: Include/exclude patterns, language filtering
|
|
19
20
|
|
|
20
21
|
### **๐๏ธ Architecture**
|
|
@@ -376,6 +377,7 @@ sunlint --validate-config .sunlint.json
|
|
|
376
377
|
## ๐ **Documentation**
|
|
377
378
|
|
|
378
379
|
- **[Configuration Guide](./docs/CONFIGURATION.md)** - Complete config options with examples
|
|
380
|
+
- **[Performance & File Limits](./docs/FILE_LIMITS_EXPLANATION.md)** - Understanding `--max-files` vs `--max-semantic-files`
|
|
379
381
|
- [ESLint Integration Guide](./docs/ESLINT_INTEGRATION.md)
|
|
380
382
|
- [CI/CD Guide](./docs/CI-CD-GUIDE.md)
|
|
381
383
|
- [Architecture](./docs/ARCHITECTURE.md)
|
|
@@ -27,14 +27,8 @@
|
|
|
27
27
|
"status": "stable",
|
|
28
28
|
"tags": ["logging", "error-handling", "severity"],
|
|
29
29
|
"engineMappings": {
|
|
30
|
-
"eslint": [
|
|
31
|
-
|
|
32
|
-
"no-alert",
|
|
33
|
-
"no-debugger"
|
|
34
|
-
],
|
|
35
|
-
"heuristic": [
|
|
36
|
-
"rules/common/C019_log_level_usage/analyzer.js"
|
|
37
|
-
]
|
|
30
|
+
"eslint": ["no-console", "no-alert", "no-debugger"],
|
|
31
|
+
"heuristic": ["rules/common/C019_log_level_usage/analyzer.js"]
|
|
38
32
|
}
|
|
39
33
|
},
|
|
40
34
|
"C006": {
|
|
@@ -635,8 +629,8 @@
|
|
|
635
629
|
"category": "security",
|
|
636
630
|
"severity": "error",
|
|
637
631
|
"languages": ["typescript", "javascript"],
|
|
638
|
-
"analyzer": "
|
|
639
|
-
"
|
|
632
|
+
"analyzer": "./rules/security/S017_use_parameterized_queries/analyzer.js",
|
|
633
|
+
"config": "./rules/security/S017_use_parameterized_queries/config.json",
|
|
640
634
|
"version": "1.0.0",
|
|
641
635
|
"status": "stable",
|
|
642
636
|
"tags": ["security", "sql-injection", "database"]
|
|
@@ -769,41 +763,122 @@
|
|
|
769
763
|
"status": "stable",
|
|
770
764
|
"tags": ["security", "directory-browsing", "information-disclosure"]
|
|
771
765
|
},
|
|
766
|
+
"S031": {
|
|
767
|
+
"name": "Set Secure flag for Session Cookies",
|
|
768
|
+
"description": "Set Secure flag for Session Cookies to protect via HTTPS. This ensures cookies are only transmitted over secure connections, preventing interception.",
|
|
769
|
+
"category": "security",
|
|
770
|
+
"severity": "error",
|
|
771
|
+
"languages": ["typescript", "javascript"],
|
|
772
|
+
"analyzer": "./rules/security/S031_secure_session_cookies/analyzer.js",
|
|
773
|
+
"config": "./rules/security/S031_secure_session_cookies/config.json",
|
|
774
|
+
"version": "1.0.0",
|
|
775
|
+
"status": "stable",
|
|
776
|
+
"tags": ["security", "cookies", "session", "https", "secure"],
|
|
777
|
+
"strategy": {
|
|
778
|
+
"preferred": "ast",
|
|
779
|
+
"fallbacks": ["ast", "regex"],
|
|
780
|
+
"accuracy": {
|
|
781
|
+
"ast": 95,
|
|
782
|
+
"regex": 85
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
"engineMappings": {
|
|
786
|
+
"heuristic": ["rules/security/S031_secure_session_cookies/analyzer.js"]
|
|
787
|
+
}
|
|
788
|
+
},
|
|
789
|
+
"S032": {
|
|
790
|
+
"name": "Set HttpOnly attribute for Session Cookies",
|
|
791
|
+
"description": "Set HttpOnly attribute for Session Cookies to prevent JavaScript access. This protects against XSS attacks by preventing client-side script access to sensitive cookies.",
|
|
792
|
+
"category": "security",
|
|
793
|
+
"severity": "error",
|
|
794
|
+
"languages": ["typescript", "javascript"],
|
|
795
|
+
"analyzer": "./rules/security/S032_httponly_session_cookies/analyzer.js",
|
|
796
|
+
"config": "./rules/security/S032_httponly_session_cookies/config.json",
|
|
797
|
+
"version": "1.0.0",
|
|
798
|
+
"status": "stable",
|
|
799
|
+
"tags": ["security", "cookies", "session", "httponly", "xss"],
|
|
800
|
+
"strategy": {
|
|
801
|
+
"preferred": "ast",
|
|
802
|
+
"fallbacks": ["ast", "regex"],
|
|
803
|
+
"accuracy": {
|
|
804
|
+
"ast": 95,
|
|
805
|
+
"regex": 85
|
|
806
|
+
}
|
|
807
|
+
},
|
|
808
|
+
"engineMappings": {
|
|
809
|
+
"heuristic": [
|
|
810
|
+
"rules/security/S032_httponly_session_cookies/analyzer.js"
|
|
811
|
+
]
|
|
812
|
+
}
|
|
813
|
+
},
|
|
772
814
|
"S033": {
|
|
773
|
-
"name": "
|
|
774
|
-
"description": "
|
|
815
|
+
"name": "Set SameSite attribute for Session Cookies",
|
|
816
|
+
"description": "Set SameSite attribute for Session Cookies to reduce CSRF risk. This prevents the browser from sending cookies along with cross-site requests, mitigating CSRF attacks.",
|
|
775
817
|
"category": "security",
|
|
776
818
|
"severity": "error",
|
|
777
819
|
"languages": ["typescript", "javascript"],
|
|
778
|
-
"analyzer": "
|
|
779
|
-
"
|
|
820
|
+
"analyzer": "./rules/security/S033_samesite_session_cookies/analyzer.js",
|
|
821
|
+
"config": "./rules/security/S033_samesite_session_cookies/config.json",
|
|
780
822
|
"version": "1.0.0",
|
|
781
823
|
"status": "stable",
|
|
782
|
-
"tags": ["security", "cookies", "samesite"]
|
|
824
|
+
"tags": ["security", "cookies", "session", "samesite", "csrf"],
|
|
825
|
+
"strategy": {
|
|
826
|
+
"preferred": "ast",
|
|
827
|
+
"fallbacks": ["ast", "regex"],
|
|
828
|
+
"accuracy": {
|
|
829
|
+
"ast": 95,
|
|
830
|
+
"regex": 85
|
|
831
|
+
}
|
|
832
|
+
},
|
|
833
|
+
"engineMappings": {
|
|
834
|
+
"heuristic": [
|
|
835
|
+
"rules/security/S033_samesite_session_cookies/analyzer.js"
|
|
836
|
+
]
|
|
837
|
+
}
|
|
783
838
|
},
|
|
784
839
|
"S034": {
|
|
785
|
-
"name": "
|
|
786
|
-
"description": "
|
|
840
|
+
"name": "Use __Host- prefix for Session Cookies",
|
|
841
|
+
"description": "Use __Host- prefix for Session Cookies to prevent subdomain sharing. The __Host- prefix ensures cookies are only sent to the exact domain that set them, preventing subdomain cookie sharing attacks.",
|
|
787
842
|
"category": "security",
|
|
788
|
-
"severity": "
|
|
843
|
+
"severity": "warning",
|
|
789
844
|
"languages": ["typescript", "javascript"],
|
|
790
|
-
"analyzer": "
|
|
791
|
-
"
|
|
845
|
+
"analyzer": "./rules/security/S034_host_prefix_session_cookies/analyzer.js",
|
|
846
|
+
"config": "./rules/security/S034_host_prefix_session_cookies/config.json",
|
|
792
847
|
"version": "1.0.0",
|
|
793
848
|
"status": "stable",
|
|
794
|
-
"tags": ["security", "cookies", "host-prefix"]
|
|
849
|
+
"tags": ["security", "cookies", "session", "host-prefix", "subdomain"],
|
|
850
|
+
"strategy": {
|
|
851
|
+
"preferred": "ast",
|
|
852
|
+
"fallbacks": ["ast", "regex"],
|
|
853
|
+
"accuracy": {
|
|
854
|
+
"ast": 95,
|
|
855
|
+
"regex": 85
|
|
856
|
+
}
|
|
857
|
+
},
|
|
858
|
+
"engineMappings": {
|
|
859
|
+
"heuristic": [
|
|
860
|
+
"rules/security/S034_host_prefix_session_cookies/analyzer.js"
|
|
861
|
+
]
|
|
862
|
+
}
|
|
795
863
|
},
|
|
796
864
|
"S035": {
|
|
797
|
-
"name": "
|
|
798
|
-
"description": "
|
|
865
|
+
"name": "Set Path attribute for Session Cookies",
|
|
866
|
+
"description": "Set Path attribute for Session Cookies to limit access scope",
|
|
799
867
|
"category": "security",
|
|
800
|
-
"severity": "
|
|
868
|
+
"severity": "warning",
|
|
801
869
|
"languages": ["typescript", "javascript"],
|
|
802
|
-
"analyzer": "
|
|
803
|
-
"eslintRule": "custom/typescript_s035",
|
|
870
|
+
"analyzer": "heuristic",
|
|
804
871
|
"version": "1.0.0",
|
|
805
872
|
"status": "stable",
|
|
806
|
-
"tags": ["security", "cookies", "path"]
|
|
873
|
+
"tags": ["security", "cookies", "path"],
|
|
874
|
+
"strategy": {
|
|
875
|
+
"defaultEngine": "heuristic",
|
|
876
|
+
"engineMappings": {
|
|
877
|
+
"heuristic": ["rules/security/S035_path_session_cookies/analyzer.js"]
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
"configPath": "rules/security/S035_path_session_cookies/config.json",
|
|
881
|
+
"analyzerPath": ["rules/security/S035_path_session_cookies/analyzer.js"]
|
|
807
882
|
},
|
|
808
883
|
"S036": {
|
|
809
884
|
"name": "No Unsafe File Include",
|
|
@@ -1196,6 +1271,36 @@
|
|
|
1196
1271
|
"accuracy": {}
|
|
1197
1272
|
}
|
|
1198
1273
|
},
|
|
1274
|
+
"C048": {
|
|
1275
|
+
"name": "Do not bypass architectural layers (controller/service/repository)",
|
|
1276
|
+
"description": "Maintain a clear layered architecture, ensuring logic and data flow are well-structured and maintainable.",
|
|
1277
|
+
"category": "naming",
|
|
1278
|
+
"severity": "warning",
|
|
1279
|
+
"languages": ["typescript", "javascript", "dart", "kotlin"],
|
|
1280
|
+
"analyzer": "./rules/common/C048_no_bypass_architectural_layers/analyzer.js",
|
|
1281
|
+
"config": "./rules/common/C048_no_bypass_architectural_layers/config.json",
|
|
1282
|
+
"version": "1.0.0",
|
|
1283
|
+
"status": "stable",
|
|
1284
|
+
"tags": ["naming", "domain", "readability"],
|
|
1285
|
+
"engineMappings": {
|
|
1286
|
+
"eslint": ["@typescript-eslint/naming-convention", "camelcase"]
|
|
1287
|
+
}
|
|
1288
|
+
},
|
|
1289
|
+
"C052": {
|
|
1290
|
+
"name": "Parsing or data transformation logic must be separated from controllers",
|
|
1291
|
+
"description": "Enforce separation of concerns โ controllers should only handle requests and delegate processing, improving testability, maintainability, and reuse.",
|
|
1292
|
+
"category": "naming",
|
|
1293
|
+
"severity": "warning",
|
|
1294
|
+
"languages": ["typescript", "javascript", "dart", "kotlin"],
|
|
1295
|
+
"analyzer": "./rules/common/C052_parsing_or_data_transformation/analyzer.js",
|
|
1296
|
+
"config": "./rules/common/C052_parsing_or_data_transformation/config.json",
|
|
1297
|
+
"version": "1.0.0",
|
|
1298
|
+
"status": "stable",
|
|
1299
|
+
"tags": ["naming", "domain", "readability"],
|
|
1300
|
+
"engineMappings": {
|
|
1301
|
+
"eslint": ["@typescript-eslint/naming-convention", "camelcase"]
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1199
1304
|
"C072": {
|
|
1200
1305
|
"id": "C072",
|
|
1201
1306
|
"name": "Single Test Behavior",
|
|
@@ -1612,6 +1717,7 @@
|
|
|
1612
1717
|
"C017",
|
|
1613
1718
|
"C018",
|
|
1614
1719
|
"C023",
|
|
1720
|
+
"C024",
|
|
1615
1721
|
"C029",
|
|
1616
1722
|
"C030",
|
|
1617
1723
|
"C035",
|
|
@@ -1619,6 +1725,8 @@
|
|
|
1619
1725
|
"C042",
|
|
1620
1726
|
"C043",
|
|
1621
1727
|
"C047",
|
|
1728
|
+
"C048",
|
|
1729
|
+
"C052",
|
|
1622
1730
|
"C072",
|
|
1623
1731
|
"C075",
|
|
1624
1732
|
"T002",
|
|
@@ -1668,6 +1776,8 @@
|
|
|
1668
1776
|
"S027",
|
|
1669
1777
|
"S029",
|
|
1670
1778
|
"S030",
|
|
1779
|
+
"S031",
|
|
1780
|
+
"S032",
|
|
1671
1781
|
"S033",
|
|
1672
1782
|
"S034",
|
|
1673
1783
|
"S035",
|
|
@@ -1777,12 +1887,12 @@
|
|
|
1777
1887
|
}
|
|
1778
1888
|
},
|
|
1779
1889
|
"metadata": {
|
|
1780
|
-
"version": "1.1.
|
|
1781
|
-
"lastUpdated": "2025-08-
|
|
1782
|
-
"totalRules":
|
|
1890
|
+
"version": "1.1.7",
|
|
1891
|
+
"lastUpdated": "2025-08-25",
|
|
1892
|
+
"totalRules": 97,
|
|
1783
1893
|
"qualityRules": 33,
|
|
1784
|
-
"securityRules":
|
|
1785
|
-
"stableRules":
|
|
1894
|
+
"securityRules": 49,
|
|
1895
|
+
"stableRules": 45,
|
|
1786
1896
|
"experimentalRules": 1,
|
|
1787
1897
|
"supportedLanguages": 4,
|
|
1788
1898
|
"features": [
|
|
@@ -1791,7 +1901,8 @@
|
|
|
1791
1901
|
"Dynamic rule configuration",
|
|
1792
1902
|
"ESLint 9.x integration",
|
|
1793
1903
|
"React rules integration",
|
|
1794
|
-
"Memory leak fixes"
|
|
1904
|
+
"Memory leak fixes",
|
|
1905
|
+
"S032 HttpOnly session cookies"
|
|
1795
1906
|
],
|
|
1796
1907
|
"consolidatedFrom": "/Users/bach.ngoc.hoai/Docs/ee/coding-quality/extensions/sunlint/config/rules/rules-registry.json"
|
|
1797
1908
|
}
|
|
@@ -10,6 +10,7 @@ const path = require('path');
|
|
|
10
10
|
const fs = require('fs');
|
|
11
11
|
const AnalysisEngineInterface = require('./interfaces/analysis-engine.interface');
|
|
12
12
|
const SunlintRuleAdapter = require('./adapters/sunlint-rule-adapter');
|
|
13
|
+
const PerformanceOptimizer = require('./performance-optimizer');
|
|
13
14
|
|
|
14
15
|
class AnalysisOrchestrator {
|
|
15
16
|
constructor() {
|
|
@@ -18,6 +19,7 @@ class AnalysisOrchestrator {
|
|
|
18
19
|
this.defaultTimeout = 30000; // 30 seconds default timeout
|
|
19
20
|
this.ruleAdapter = SunlintRuleAdapter.getInstance();
|
|
20
21
|
this.enginesConfigPath = path.join(__dirname, '..', 'config', 'engines', 'engines.json');
|
|
22
|
+
this.performanceOptimizer = new PerformanceOptimizer();
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -171,14 +173,35 @@ class AnalysisOrchestrator {
|
|
|
171
173
|
throw new Error('No analysis engines registered');
|
|
172
174
|
}
|
|
173
175
|
|
|
176
|
+
// Initialize performance optimizer
|
|
177
|
+
await this.performanceOptimizer.initialize(config);
|
|
178
|
+
|
|
179
|
+
// Apply performance optimizations to files and rules
|
|
180
|
+
const { optimizedFiles, optimizedRules, performanceMetrics } =
|
|
181
|
+
await this.performanceOptimizer.optimizeAnalysis(
|
|
182
|
+
options.files || [options.input],
|
|
183
|
+
rulesToRun,
|
|
184
|
+
config
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
if (!options.quiet) {
|
|
188
|
+
console.log(chalk.cyan(`๐ Analyzing ${optimizedRules.length} rules on ${optimizedFiles.length} files...`));
|
|
189
|
+
if (performanceMetrics.filteredFiles > 0) {
|
|
190
|
+
console.log(chalk.gray(` ๐ฆ Filtered ${performanceMetrics.filteredFiles} files for performance`));
|
|
191
|
+
}
|
|
192
|
+
if (performanceMetrics.ruleBatches > 1) {
|
|
193
|
+
console.log(chalk.gray(` ๐ Using ${performanceMetrics.ruleBatches} rule batches`));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
174
197
|
// Group rules by their preferred engines
|
|
175
|
-
const engineGroups = this.groupRulesByEngine(
|
|
198
|
+
const engineGroups = this.groupRulesByEngine(optimizedRules, config);
|
|
176
199
|
|
|
177
200
|
if (!options.quiet) {
|
|
178
|
-
console.log(chalk.cyan(
|
|
201
|
+
console.log(chalk.cyan(`๐ Running analysis across ${engineGroups.size} engines...`));
|
|
179
202
|
}
|
|
180
203
|
|
|
181
|
-
// Run analysis on each engine
|
|
204
|
+
// Run analysis on each engine with batching
|
|
182
205
|
const results = [];
|
|
183
206
|
for (const [engineName, rules] of engineGroups) {
|
|
184
207
|
const engine = this.engines.get(engineName);
|
|
@@ -187,35 +210,64 @@ class AnalysisOrchestrator {
|
|
|
187
210
|
continue;
|
|
188
211
|
}
|
|
189
212
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
engine,
|
|
197
|
-
options.files || [options.input],
|
|
198
|
-
rules,
|
|
199
|
-
options
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
results.push({
|
|
203
|
-
engine: engineName,
|
|
204
|
-
rules: rules.map(r => r.id),
|
|
205
|
-
...engineResult
|
|
206
|
-
});
|
|
213
|
+
// Process rules in batches for performance
|
|
214
|
+
const ruleBatches = this.performanceOptimizer.createRuleBatches(rules, config);
|
|
215
|
+
|
|
216
|
+
for (let i = 0; i < ruleBatches.length; i++) {
|
|
217
|
+
const batch = ruleBatches[i];
|
|
218
|
+
const batchNumber = i + 1;
|
|
207
219
|
|
|
208
|
-
if (!options.quiet) {
|
|
209
|
-
|
|
210
|
-
|
|
220
|
+
if (!options.quiet && ruleBatches.length > 1) {
|
|
221
|
+
console.log(chalk.blue(`โ๏ธ ${engineName} - Batch ${batchNumber}/${ruleBatches.length}: ${batch.length} rules`));
|
|
222
|
+
} else if (!options.quiet) {
|
|
223
|
+
console.log(chalk.blue(`โ๏ธ Running ${batch.length} rules on ${engineName} engine...`));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
const engineResult = await this.runEngineWithOptimizations(
|
|
228
|
+
engine,
|
|
229
|
+
optimizedFiles,
|
|
230
|
+
batch,
|
|
231
|
+
options,
|
|
232
|
+
{ batchNumber, totalBatches: ruleBatches.length }
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
results.push({
|
|
236
|
+
engine: engineName,
|
|
237
|
+
batch: batchNumber,
|
|
238
|
+
rules: batch.map(r => r.id),
|
|
239
|
+
...engineResult
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
if (!options.quiet) {
|
|
243
|
+
const violationCount = this.countViolations(engineResult);
|
|
244
|
+
console.log(chalk.blue(`โ
${engineName} batch ${batchNumber}: ${violationCount} violations found`));
|
|
245
|
+
}
|
|
246
|
+
} catch (error) {
|
|
247
|
+
// Enhanced error recovery with batch context
|
|
248
|
+
const errorInfo = this.performanceOptimizer.handleAnalysisError(error, {
|
|
249
|
+
engine: engineName,
|
|
250
|
+
batch: batchNumber,
|
|
251
|
+
rules: batch.map(r => r.id),
|
|
252
|
+
files: optimizedFiles.length
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
console.error(chalk.red(`โ Engine ${engineName} batch ${batchNumber} failed:`), errorInfo.message);
|
|
256
|
+
|
|
257
|
+
if (errorInfo.shouldRetry && !options.noRetry) {
|
|
258
|
+
console.log(chalk.yellow(`๐ Retrying with reduced batch size...`));
|
|
259
|
+
// Split batch and retry - implement recursive retry logic here
|
|
260
|
+
}
|
|
261
|
+
// Continue with other batches instead of failing completely
|
|
211
262
|
}
|
|
212
|
-
} catch (error) {
|
|
213
|
-
console.error(chalk.red(`โ Engine ${engineName} failed:`), error.message);
|
|
214
|
-
// Continue with other engines instead of failing completely
|
|
215
263
|
}
|
|
216
264
|
}
|
|
217
265
|
|
|
218
|
-
|
|
266
|
+
// Merge results and add performance metrics
|
|
267
|
+
const mergedResults = this.mergeEngineResults(results, options);
|
|
268
|
+
mergedResults.performance = performanceMetrics;
|
|
269
|
+
|
|
270
|
+
return mergedResults;
|
|
219
271
|
|
|
220
272
|
} catch (error) {
|
|
221
273
|
console.error(chalk.red('โ Analysis orchestration failed:'), error.message);
|
|
@@ -393,23 +445,68 @@ class AnalysisOrchestrator {
|
|
|
393
445
|
}
|
|
394
446
|
|
|
395
447
|
/**
|
|
396
|
-
* Run engine analysis with timeout protection
|
|
448
|
+
* Run engine analysis with timeout protection and performance optimizations
|
|
397
449
|
* Following Rule C006: Verb-noun naming
|
|
398
450
|
* @param {AnalysisEngineInterface} engine - Engine to run
|
|
399
451
|
* @param {string[]} files - Files to analyze
|
|
400
452
|
* @param {Object[]} rules - Rules to apply
|
|
401
453
|
* @param {Object} options - Analysis options
|
|
454
|
+
* @param {Object} batchInfo - Batch context information
|
|
402
455
|
* @returns {Promise<Object>} Engine results
|
|
403
456
|
*/
|
|
404
|
-
async
|
|
405
|
-
|
|
457
|
+
async runEngineWithOptimizations(engine, files, rules, options, batchInfo = {}) {
|
|
458
|
+
// Dynamic timeout based on file count and rules
|
|
459
|
+
const adaptiveTimeout = this.performanceOptimizer.calculateAdaptiveTimeout(
|
|
460
|
+
files.length,
|
|
461
|
+
rules.length,
|
|
462
|
+
options.timeout || this.defaultTimeout
|
|
463
|
+
);
|
|
406
464
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
465
|
+
const enhancedOptions = {
|
|
466
|
+
...options,
|
|
467
|
+
timeout: adaptiveTimeout,
|
|
468
|
+
batchInfo
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
try {
|
|
472
|
+
return await Promise.race([
|
|
473
|
+
engine.analyze(files, rules, enhancedOptions),
|
|
474
|
+
new Promise((_, reject) =>
|
|
475
|
+
setTimeout(() => reject(new Error(
|
|
476
|
+
`Engine ${engine.name} batch ${batchInfo.batchNumber || 1} timed out after ${adaptiveTimeout}ms`
|
|
477
|
+
)), adaptiveTimeout)
|
|
478
|
+
)
|
|
479
|
+
]);
|
|
480
|
+
} catch (error) {
|
|
481
|
+
// Enhanced error context for debugging
|
|
482
|
+
const errorContext = {
|
|
483
|
+
engine: engine.name,
|
|
484
|
+
filesCount: files.length,
|
|
485
|
+
rulesCount: rules.length,
|
|
486
|
+
timeout: adaptiveTimeout,
|
|
487
|
+
batch: batchInfo
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// Wrap error with context
|
|
491
|
+
const enhancedError = new Error(`${error.message} (Context: ${JSON.stringify(errorContext)})`);
|
|
492
|
+
enhancedError.originalError = error;
|
|
493
|
+
enhancedError.context = errorContext;
|
|
494
|
+
|
|
495
|
+
throw enhancedError;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Run engine analysis with timeout protection (legacy method for backward compatibility)
|
|
501
|
+
* Following Rule C006: Verb-noun naming
|
|
502
|
+
* @param {AnalysisEngineInterface} engine - Engine to run
|
|
503
|
+
* @param {string[]} files - Files to analyze
|
|
504
|
+
* @param {Object[]} rules - Rules to apply
|
|
505
|
+
* @param {Object} options - Analysis options
|
|
506
|
+
* @returns {Promise<Object>} Engine results
|
|
507
|
+
*/
|
|
508
|
+
async runEngineWithTimeout(engine, files, rules, options) {
|
|
509
|
+
return this.runEngineWithOptimizations(engine, files, rules, options);
|
|
413
510
|
}
|
|
414
511
|
|
|
415
512
|
/**
|
|
@@ -425,6 +522,7 @@ class AnalysisOrchestrator {
|
|
|
425
522
|
results: [],
|
|
426
523
|
summary: {
|
|
427
524
|
totalEngines: engineResults.length,
|
|
525
|
+
totalBatches: engineResults.length,
|
|
428
526
|
totalViolations: 0,
|
|
429
527
|
totalFiles: 0,
|
|
430
528
|
engines: {}
|
|
@@ -436,8 +534,13 @@ class AnalysisOrchestrator {
|
|
|
436
534
|
}
|
|
437
535
|
};
|
|
438
536
|
|
|
537
|
+
// Track unique engines for summary
|
|
538
|
+
const uniqueEngines = new Set();
|
|
539
|
+
|
|
439
540
|
// Combine results from all engines
|
|
440
541
|
for (const engineResult of engineResults) {
|
|
542
|
+
uniqueEngines.add(engineResult.engine);
|
|
543
|
+
|
|
441
544
|
// Add engine-specific results
|
|
442
545
|
if (engineResult.results) {
|
|
443
546
|
mergedResults.results.push(...engineResult.results);
|
|
@@ -445,16 +548,30 @@ class AnalysisOrchestrator {
|
|
|
445
548
|
|
|
446
549
|
// Track engine statistics
|
|
447
550
|
const violationCount = this.countViolations(engineResult);
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
551
|
+
const engineName = engineResult.engine;
|
|
552
|
+
|
|
553
|
+
if (!mergedResults.summary.engines[engineName]) {
|
|
554
|
+
mergedResults.summary.engines[engineName] = {
|
|
555
|
+
rules: [],
|
|
556
|
+
violations: 0,
|
|
557
|
+
files: 0,
|
|
558
|
+
batches: 0
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Accumulate engine statistics across batches
|
|
563
|
+
mergedResults.summary.engines[engineName].rules.push(...(engineResult.rules || []));
|
|
564
|
+
mergedResults.summary.engines[engineName].violations += violationCount;
|
|
565
|
+
mergedResults.summary.engines[engineName].files += engineResult.filesAnalyzed || 0;
|
|
566
|
+
mergedResults.summary.engines[engineName].batches += 1;
|
|
453
567
|
|
|
454
568
|
mergedResults.summary.totalViolations += violationCount;
|
|
455
569
|
mergedResults.summary.totalFiles += engineResult.filesAnalyzed || 0;
|
|
456
570
|
}
|
|
457
571
|
|
|
572
|
+
// Update unique engine count
|
|
573
|
+
mergedResults.summary.totalEngines = uniqueEngines.size;
|
|
574
|
+
|
|
458
575
|
return mergedResults;
|
|
459
576
|
}
|
|
460
577
|
|
|
@@ -517,7 +634,7 @@ class AnalysisOrchestrator {
|
|
|
517
634
|
}
|
|
518
635
|
|
|
519
636
|
/**
|
|
520
|
-
* Cleanup all engines
|
|
637
|
+
* Cleanup all engines and performance optimizer
|
|
521
638
|
* Following Rule C006: Verb-noun naming
|
|
522
639
|
* @returns {Promise<void>}
|
|
523
640
|
*/
|
|
@@ -529,6 +646,14 @@ class AnalysisOrchestrator {
|
|
|
529
646
|
console.warn(chalk.yellow(`โ ๏ธ Failed to cleanup engine ${engine.id}:`), error.message);
|
|
530
647
|
}
|
|
531
648
|
}
|
|
649
|
+
|
|
650
|
+
// Cleanup performance optimizer
|
|
651
|
+
try {
|
|
652
|
+
await this.performanceOptimizer.cleanup();
|
|
653
|
+
} catch (error) {
|
|
654
|
+
console.warn(chalk.yellow(`โ ๏ธ Failed to cleanup performance optimizer:`), error.message);
|
|
655
|
+
}
|
|
656
|
+
|
|
532
657
|
this.initialized = false;
|
|
533
658
|
console.log(chalk.blue('๐งน Engine cleanup completed'));
|
|
534
659
|
}
|