@sun-asterisk/sunlint 1.2.2 โ†’ 1.3.1

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.
Files changed (124) hide show
  1. package/CHANGELOG.md +107 -1
  2. package/CONTRIBUTING.md +1654 -66
  3. package/README.md +19 -6
  4. package/config/ci-cd.json +54 -0
  5. package/config/development.json +56 -0
  6. package/config/engines/engines-enhanced.json +86 -0
  7. package/config/engines/semantic-config.json +114 -0
  8. package/config/eslint-rule-mapping.json +50 -38
  9. package/config/large-project.json +143 -0
  10. package/config/presets/all.json +0 -1
  11. package/config/release.json +70 -0
  12. package/config/rule-analysis-strategies.js +23 -4
  13. package/config/rules/S027-categories.json +122 -0
  14. package/config/rules/enhanced-rules-registry.json +2564 -0
  15. package/config/rules/rules-registry-generated.json +785 -837
  16. package/config/rules/rules-registry.json +13 -1
  17. package/core/adapters/sunlint-rule-adapter.js +25 -30
  18. package/core/analysis-orchestrator.js +42 -2
  19. package/core/categories.js +52 -0
  20. package/core/category-constants.js +39 -0
  21. package/core/cli-action-handler.js +53 -32
  22. package/core/cli-program.js +11 -3
  23. package/core/config-manager.js +111 -0
  24. package/core/config-merger.js +88 -0
  25. package/core/constants/categories.js +168 -0
  26. package/core/constants/defaults.js +165 -0
  27. package/core/constants/engines.js +185 -0
  28. package/core/constants/index.js +30 -0
  29. package/core/constants/rules.js +215 -0
  30. package/core/enhanced-rules-registry.js +3 -3
  31. package/core/file-targeting-service.js +128 -7
  32. package/core/interfaces/rule-plugin.interface.js +207 -0
  33. package/core/plugin-manager.js +448 -0
  34. package/core/rule-selection-service.js +42 -15
  35. package/core/semantic-engine.js +658 -0
  36. package/core/semantic-rule-base.js +433 -0
  37. package/core/unified-rule-registry.js +484 -0
  38. package/docs/COMMAND-EXAMPLES.md +134 -0
  39. package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
  40. package/docs/LARGE-PROJECT-GUIDE.md +324 -0
  41. package/engines/core/base-engine.js +249 -0
  42. package/engines/engine-factory.js +275 -0
  43. package/engines/eslint-engine.js +171 -19
  44. package/engines/heuristic-engine.js +569 -78
  45. package/integrations/eslint/plugin/index.js +26 -28
  46. package/origin-rules/common-en.md +8 -8
  47. package/package.json +10 -6
  48. package/rules/common/C003_no_vague_abbreviations/analyzer.js +1 -1
  49. package/rules/common/C017_constructor_logic/analyzer.js +254 -17
  50. package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
  51. package/rules/common/C029_catch_block_logging/analyzer.js +17 -5
  52. package/rules/common/C033_separate_service_repository/README.md +78 -0
  53. package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
  54. package/rules/common/C033_separate_service_repository/config.json +50 -0
  55. package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
  56. package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
  57. package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
  58. package/rules/common/C035_error_logging_context/analyzer.js +230 -0
  59. package/rules/common/C035_error_logging_context/config.json +54 -0
  60. package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
  61. package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
  62. package/rules/common/C040_centralized_validation/analyzer.js +165 -0
  63. package/rules/common/C040_centralized_validation/config.json +46 -0
  64. package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
  65. package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
  66. package/rules/common/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
  67. package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
  68. package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -0
  69. package/rules/common/{C076_single_test_behavior โ†’ C072_single_test_behavior}/analyzer.js +6 -6
  70. package/rules/common/C076_explicit_function_types/README.md +30 -0
  71. package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
  72. package/rules/common/C076_explicit_function_types/config.json +15 -0
  73. package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
  74. package/rules/index.js +8 -0
  75. package/rules/parser/rule-parser.js +13 -2
  76. package/rules/security/S005_no_origin_auth/README.md +226 -0
  77. package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
  78. package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
  79. package/rules/security/S005_no_origin_auth/config.json +85 -0
  80. package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
  81. package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
  82. package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
  83. package/rules/security/S007_no_plaintext_otp/README.md +198 -0
  84. package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
  85. package/rules/security/S007_no_plaintext_otp/config.json +79 -0
  86. package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
  87. package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
  88. package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
  89. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
  90. package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
  91. package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
  92. package/scripts/category-manager.js +150 -0
  93. package/scripts/generate-rules-registry.js +88 -0
  94. package/scripts/migrate-rule-registry.js +157 -0
  95. package/scripts/prepare-release.sh +1 -1
  96. package/scripts/validate-system.js +48 -0
  97. package/.sunlint.json +0 -35
  98. package/config/README.md +0 -88
  99. package/config/engines/eslint-rule-mapping.json +0 -74
  100. package/config/schemas/sunlint-schema.json +0 -0
  101. package/config/testing/test-s005-working.ts +0 -22
  102. package/core/multi-rule-runner.js +0 -0
  103. package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
  104. package/docs/FUTURE_PACKAGES.md +0 -83
  105. package/docs/HEURISTIC_VS_AI.md +0 -113
  106. package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
  107. package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
  108. package/docs/RELEASE_GUIDE.md +0 -230
  109. package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
  110. package/engines/tree-sitter-parser.js +0 -0
  111. package/engines/universal-ast-engine.js +0 -0
  112. package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
  113. package/rules/common/C029_catch_block_logging/analyzer-backup.js +0 -426
  114. package/rules/common/C029_catch_block_logging/analyzer-fixed.js +0 -130
  115. package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +0 -487
  116. package/rules/common/C029_catch_block_logging/analyzer-simple.js +0 -110
  117. package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +0 -441
  118. package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +0 -127
  119. package/rules/common/C029_catch_block_logging/ast-analyzer.js +0 -133
  120. package/rules/common/C029_catch_block_logging/cfg-analyzer.js +0 -408
  121. package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +0 -454
  122. package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +0 -700
  123. package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +0 -568
  124. package/rules/common/C029_catch_block_logging/semantic-analyzer.js +0 -459
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Base Engine Abstract Class
3
+ * Following Rule C005: Single responsibility - Base engine functionality
4
+ * Following Rule C014: Dependency injection - Plugin architecture
5
+ */
6
+
7
+ const EventEmitter = require('events');
8
+
9
+ class BaseEngine extends EventEmitter {
10
+ constructor(name, version, supportedLanguages = []) {
11
+ super();
12
+ this.name = name;
13
+ this.version = version;
14
+ this.supportedLanguages = supportedLanguages;
15
+ this.initialized = false;
16
+ this.verbose = false;
17
+ this.ruleRegistry = new Map();
18
+ this.pluginManager = null;
19
+ }
20
+
21
+ /**
22
+ * Set plugin manager for this engine
23
+ * @param {PluginManager} pluginManager - Plugin manager instance
24
+ */
25
+ setPluginManager(pluginManager) {
26
+ this.pluginManager = pluginManager;
27
+ }
28
+
29
+ /**
30
+ * Abstract method - must be implemented by subclasses
31
+ */
32
+ async initialize(config) {
33
+ this.verbose = config.verbose || false;
34
+
35
+ // Load rules from plugin manager if available
36
+ if (this.pluginManager) {
37
+ const engineRules = await this.pluginManager.loadRulesForEngine(this.name, config);
38
+ this.ruleRegistry = engineRules;
39
+
40
+ if (this.verbose) {
41
+ console.log(`๐Ÿ”ง [${this.name}] Loaded ${engineRules.size} rules from Plugin Manager`);
42
+ }
43
+ }
44
+
45
+ this.initialized = true;
46
+ }
47
+
48
+ /**
49
+ * Abstract method - must be implemented by subclasses
50
+ */
51
+ async analyze(files, rules, options) {
52
+ throw new Error('analyze() must be implemented by subclass');
53
+ }
54
+
55
+ /**
56
+ * Analyze files with a specific rule plugin
57
+ * @param {string} ruleId - Rule identifier
58
+ * @param {Array} files - Files to analyze
59
+ * @param {string} language - Programming language
60
+ * @param {Object} options - Analysis options
61
+ * @returns {Array} Array of violations
62
+ */
63
+ async analyzeWithRule(ruleId, files, language, options = {}) {
64
+ const ruleInfo = this.ruleRegistry.get(ruleId);
65
+
66
+ if (!ruleInfo) {
67
+ if (this.verbose) {
68
+ console.warn(`โš ๏ธ [${this.name}] Rule ${ruleId} not found`);
69
+ }
70
+ return [];
71
+ }
72
+
73
+ try {
74
+ const violations = await ruleInfo.plugin.analyze(files, language, {
75
+ ...options,
76
+ engine: this.name,
77
+ metadata: ruleInfo.metadata
78
+ });
79
+
80
+ // Ensure violations have correct rule ID
81
+ return violations.map(violation => ({
82
+ ...violation,
83
+ ruleId: ruleId,
84
+ engine: this.name,
85
+ pluginType: ruleInfo.type
86
+ }));
87
+
88
+ } catch (error) {
89
+ if (this.verbose) {
90
+ console.error(`โŒ [${this.name}] Error analyzing with rule ${ruleId}: ${error.message}`);
91
+ }
92
+ return [];
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Check if a rule is supported by this engine
98
+ * @param {string} ruleId - Rule ID to check
99
+ * @returns {boolean} True if rule is supported
100
+ */
101
+ isRuleSupported(ruleId) {
102
+ return this.ruleRegistry.has(ruleId);
103
+ }
104
+
105
+ /**
106
+ * Get engine information
107
+ * @returns {Object} Engine metadata
108
+ */
109
+ getEngineInfo() {
110
+ return {
111
+ name: this.name,
112
+ version: this.version,
113
+ supportedLanguages: this.supportedLanguages,
114
+ initialized: this.initialized,
115
+ rulesLoaded: this.ruleRegistry.size
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Register a rule with the engine
121
+ * @param {string} ruleId - Rule identifier
122
+ * @param {Object} ruleInfo - Rule information and analyzer
123
+ */
124
+ registerRule(ruleId, ruleInfo) {
125
+ this.ruleRegistry.set(ruleId, ruleInfo);
126
+ this.emit('ruleRegistered', { ruleId, engine: this.name });
127
+ }
128
+
129
+ /**
130
+ * Unregister a rule from the engine
131
+ * @param {string} ruleId - Rule identifier
132
+ */
133
+ unregisterRule(ruleId) {
134
+ const removed = this.ruleRegistry.delete(ruleId);
135
+ if (removed) {
136
+ this.emit('ruleUnregistered', { ruleId, engine: this.name });
137
+ }
138
+ return removed;
139
+ }
140
+
141
+ /**
142
+ * Get all registered rules
143
+ * @returns {string[]} Array of rule IDs
144
+ */
145
+ getRegisteredRules() {
146
+ return Array.from(this.ruleRegistry.keys());
147
+ }
148
+
149
+ /**
150
+ * Set plugin manager
151
+ * @param {Object} pluginManager - Plugin manager instance
152
+ */
153
+ setPluginManager(pluginManager) {
154
+ this.pluginManager = pluginManager;
155
+ }
156
+
157
+ /**
158
+ * Load rules using plugin manager
159
+ * @param {Object} config - Configuration options
160
+ */
161
+ async loadRules(config = {}) {
162
+ if (!this.pluginManager) {
163
+ throw new Error('Plugin manager not set');
164
+ }
165
+
166
+ const rules = await this.pluginManager.loadRulesForEngine(this.name, config);
167
+
168
+ for (const [ruleId, ruleInfo] of rules) {
169
+ this.registerRule(ruleId, ruleInfo);
170
+ }
171
+
172
+ if (this.verbose) {
173
+ console.log(`๐Ÿ“š [${this.name}] Loaded ${rules.size} rules`);
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Cleanup engine resources
179
+ */
180
+ async cleanup() {
181
+ this.ruleRegistry.clear();
182
+ this.initialized = false;
183
+ this.emit('cleanup', { engine: this.name });
184
+
185
+ if (this.verbose) {
186
+ console.log(`๐Ÿ”ง [${this.name}] Engine cleanup completed`);
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Group files by programming language
192
+ * @param {string[]} files - Files to group
193
+ * @returns {Object} Files grouped by language
194
+ */
195
+ groupFilesByLanguage(files) {
196
+ const groups = {};
197
+
198
+ for (const file of files) {
199
+ const language = this.detectLanguage(file);
200
+ if (!groups[language]) {
201
+ groups[language] = [];
202
+ }
203
+ groups[language].push(file);
204
+ }
205
+
206
+ return groups;
207
+ }
208
+
209
+ /**
210
+ * Detect programming language from file extension
211
+ * @param {string} filePath - File path
212
+ * @returns {string} Detected language
213
+ */
214
+ detectLanguage(filePath) {
215
+ const path = require('path');
216
+ const ext = path.extname(filePath).toLowerCase();
217
+
218
+ const languageMap = {
219
+ '.ts': 'typescript',
220
+ '.tsx': 'typescript',
221
+ '.js': 'javascript',
222
+ '.jsx': 'javascript',
223
+ '.dart': 'dart',
224
+ '.swift': 'swift',
225
+ '.kt': 'kotlin',
226
+ '.kts': 'kotlin',
227
+ '.java': 'java',
228
+ '.py': 'python',
229
+ '.go': 'go',
230
+ '.rs': 'rust',
231
+ '.php': 'php',
232
+ '.rb': 'ruby'
233
+ };
234
+
235
+ return languageMap[ext] || 'unknown';
236
+ }
237
+
238
+ /**
239
+ * Check if language is supported
240
+ * @param {string} language - Language to check
241
+ * @returns {boolean} True if supported
242
+ */
243
+ isLanguageSupported(language) {
244
+ return this.supportedLanguages.includes(language) ||
245
+ this.supportedLanguages.includes('all');
246
+ }
247
+ }
248
+
249
+ module.exports = BaseEngine;
@@ -0,0 +1,275 @@
1
+ /**
2
+ * Engine Factory
3
+ * Creates and manages engine instances with plugin support
4
+ * Following Rule C005: Single responsibility - Engine creation
5
+ */
6
+
7
+ const PluginManager = require('../core/plugin-manager');
8
+ const BaseEngine = require('./core/base-engine');
9
+
10
+ // Engine imports
11
+ const HeuristicEngine = require('../engines/heuristic-engine');
12
+ const ESLintEngine = require('../engines/eslint-engine');
13
+ const OpenAIEngine = require('../engines/openai-engine');
14
+
15
+ class EngineFactory {
16
+ constructor() {
17
+ this.pluginManager = null;
18
+ this.engines = new Map();
19
+ this.verbose = false;
20
+ }
21
+
22
+ /**
23
+ * Initialize factory with plugin manager
24
+ * @param {Object} config - Configuration options
25
+ */
26
+ async initialize(config = {}) {
27
+ this.verbose = config.verbose || false;
28
+
29
+ // Initialize plugin manager
30
+ this.pluginManager = new PluginManager();
31
+ await this.pluginManager.initialize(config);
32
+
33
+ if (this.verbose) {
34
+ console.log('๐Ÿญ Engine Factory initialized');
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Create an engine instance
40
+ * @param {string} engineType - Type of engine to create
41
+ * @param {Object} config - Engine configuration
42
+ * @returns {BaseEngine} Engine instance
43
+ */
44
+ async createEngine(engineType, config = {}) {
45
+ const engineConfig = {
46
+ ...config,
47
+ verbose: this.verbose,
48
+ pluginManager: this.pluginManager
49
+ };
50
+
51
+ let engine = null;
52
+
53
+ switch (engineType.toLowerCase()) {
54
+ case 'heuristic':
55
+ engine = new HeuristicEngine();
56
+ break;
57
+ case 'eslint':
58
+ engine = new ESLintEngine();
59
+ break;
60
+ case 'openai':
61
+ engine = new OpenAIEngine();
62
+ break;
63
+ default:
64
+ throw new Error(`Unknown engine type: ${engineType}`);
65
+ }
66
+
67
+ // Set plugin manager and initialize
68
+ if (engine instanceof BaseEngine) {
69
+ engine.setPluginManager(this.pluginManager);
70
+ await engine.initialize(engineConfig);
71
+ }
72
+
73
+ // Cache engine instance
74
+ this.engines.set(engineType, engine);
75
+
76
+ if (this.verbose) {
77
+ console.log(`๐Ÿ”ง Created ${engineType} engine with ${engine.ruleRegistry.size} rules`);
78
+ }
79
+
80
+ return engine;
81
+ }
82
+
83
+ /**
84
+ * Get an existing engine or create a new one
85
+ * @param {string} engineType - Type of engine
86
+ * @param {Object} config - Engine configuration
87
+ * @returns {BaseEngine} Engine instance
88
+ */
89
+ async getEngine(engineType, config = {}) {
90
+ if (this.engines.has(engineType)) {
91
+ return this.engines.get(engineType);
92
+ }
93
+
94
+ return await this.createEngine(engineType, config);
95
+ }
96
+
97
+ /**
98
+ * Get all available engines
99
+ * @returns {Map} Map of engine type to engine instance
100
+ */
101
+ getAllEngines() {
102
+ return this.engines;
103
+ }
104
+
105
+ /**
106
+ * Check if engine type is supported
107
+ * @param {string} engineType - Engine type to check
108
+ * @returns {boolean} True if supported
109
+ */
110
+ isEngineSupported(engineType) {
111
+ const supportedEngines = ['heuristic', 'eslint', 'openai'];
112
+ return supportedEngines.includes(engineType.toLowerCase());
113
+ }
114
+
115
+ /**
116
+ * Get engine metadata
117
+ * @param {string} engineType - Engine type
118
+ * @returns {Object|null} Engine metadata
119
+ */
120
+ getEngineMetadata(engineType) {
121
+ const engine = this.engines.get(engineType);
122
+ return engine ? engine.getMetadata() : null;
123
+ }
124
+
125
+ /**
126
+ * Reload custom rules in all engines
127
+ * @param {Object} config - Configuration options
128
+ */
129
+ async reloadCustomRules(config = {}) {
130
+ if (this.pluginManager) {
131
+ await this.pluginManager.reloadCustomRules(config);
132
+
133
+ // Reinitialize all engines to pick up new rules
134
+ for (const [engineType, engine] of this.engines) {
135
+ if (engine instanceof BaseEngine) {
136
+ await engine.initialize({ ...config, verbose: this.verbose });
137
+ }
138
+ }
139
+
140
+ if (this.verbose) {
141
+ console.log('๐Ÿ”„ Reloaded custom rules in all engines');
142
+ }
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Get compatible engines for a language
148
+ * @param {string} language - Programming language
149
+ * @returns {Array} Array of compatible engine types
150
+ */
151
+ getCompatibleEngines(language) {
152
+ const compatible = [];
153
+
154
+ for (const [engineType, engine] of this.engines) {
155
+ if (engine.supportedLanguages.includes(language.toLowerCase())) {
156
+ compatible.push(engineType);
157
+ }
158
+ }
159
+
160
+ return compatible;
161
+ }
162
+
163
+ /**
164
+ * Create multiple engines from configuration
165
+ * @param {Object} engineConfig - Engine configuration object
166
+ * @returns {Map} Map of created engines
167
+ */
168
+ async createEnginesFromConfig(engineConfig = {}) {
169
+ const engines = new Map();
170
+
171
+ for (const [engineType, config] of Object.entries(engineConfig)) {
172
+ if (this.isEngineSupported(engineType)) {
173
+ try {
174
+ const engine = await this.createEngine(engineType, config);
175
+ engines.set(engineType, engine);
176
+ } catch (error) {
177
+ if (this.verbose) {
178
+ console.warn(`โš ๏ธ Failed to create ${engineType} engine: ${error.message}`);
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ return engines;
185
+ }
186
+
187
+ /**
188
+ * Analyze files using best engine for language
189
+ * @param {Array} files - Files to analyze
190
+ * @param {string} language - Programming language
191
+ * @param {Array} rules - Rules to apply
192
+ * @param {Object} options - Analysis options
193
+ * @returns {Array} Array of violations
194
+ */
195
+ async analyzeWithBestEngine(files, language, rules, options = {}) {
196
+ const compatibleEngines = this.getCompatibleEngines(language);
197
+
198
+ if (compatibleEngines.length === 0) {
199
+ throw new Error(`No compatible engines found for language: ${language}`);
200
+ }
201
+
202
+ // Priority: heuristic > eslint > openai
203
+ const enginePriority = ['heuristic', 'eslint', 'openai'];
204
+ const bestEngine = enginePriority.find(type => compatibleEngines.includes(type));
205
+
206
+ if (!bestEngine) {
207
+ throw new Error(`No suitable engine found for language: ${language}`);
208
+ }
209
+
210
+ const engine = await this.getEngine(bestEngine, options);
211
+ return await engine.analyze(files, rules, { ...options, language });
212
+ }
213
+
214
+ /**
215
+ * Cleanup all engines and plugin manager
216
+ */
217
+ async cleanup() {
218
+ // Cleanup all engines
219
+ for (const [engineType, engine] of this.engines) {
220
+ try {
221
+ if (engine.cleanup) {
222
+ await engine.cleanup();
223
+ }
224
+ } catch (error) {
225
+ if (this.verbose) {
226
+ console.warn(`โš ๏ธ Error cleaning up ${engineType} engine: ${error.message}`);
227
+ }
228
+ }
229
+ }
230
+
231
+ // Cleanup plugin manager
232
+ if (this.pluginManager) {
233
+ await this.pluginManager.cleanup();
234
+ }
235
+
236
+ this.engines.clear();
237
+ this.pluginManager = null;
238
+
239
+ if (this.verbose) {
240
+ console.log('๐Ÿงน Engine Factory cleanup completed');
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Get factory statistics
246
+ * @returns {Object} Factory statistics
247
+ */
248
+ getStatistics() {
249
+ const stats = {
250
+ engineCount: this.engines.size,
251
+ engines: {},
252
+ totalRules: 0
253
+ };
254
+
255
+ for (const [engineType, engine] of this.engines) {
256
+ const metadata = engine.getMetadata ? engine.getMetadata() : {};
257
+ stats.engines[engineType] = {
258
+ ...metadata,
259
+ ruleCount: engine.ruleRegistry ? engine.ruleRegistry.size : 0
260
+ };
261
+ stats.totalRules += stats.engines[engineType].ruleCount;
262
+ }
263
+
264
+ if (this.pluginManager) {
265
+ stats.pluginManager = {
266
+ totalPlugins: this.pluginManager.getAllPlugins().size,
267
+ customRules: this.pluginManager.customRules.size
268
+ };
269
+ }
270
+
271
+ return stats;
272
+ }
273
+ }
274
+
275
+ module.exports = EngineFactory;