@sfdxy/mule-lint 1.18.0 → 1.19.0

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 (245) hide show
  1. package/README.md +154 -150
  2. package/dist/bin/mule-lint-mcp.js.map +1 -1
  3. package/dist/bin/mule-lint.js +4 -3
  4. package/dist/bin/mule-lint.js.map +1 -1
  5. package/dist/package.json +26 -15
  6. package/dist/src/core/ComplexityCalculator.d.ts.map +1 -1
  7. package/dist/src/core/ComplexityCalculator.js.map +1 -1
  8. package/dist/src/core/FileScanner.d.ts.map +1 -1
  9. package/dist/src/core/FileScanner.js.map +1 -1
  10. package/dist/src/core/MetricsAggregator.d.ts.map +1 -1
  11. package/dist/src/core/MetricsAggregator.js.map +1 -1
  12. package/dist/src/core/MetricsCollector.d.ts.map +1 -1
  13. package/dist/src/core/MetricsCollector.js +1 -2
  14. package/dist/src/core/MetricsCollector.js.map +1 -1
  15. package/dist/src/core/QualityGateEvaluator.d.ts.map +1 -1
  16. package/dist/src/core/QualityGateEvaluator.js.map +1 -1
  17. package/dist/src/core/XPathHelper.d.ts +9 -1
  18. package/dist/src/core/XPathHelper.d.ts.map +1 -1
  19. package/dist/src/core/XPathHelper.js +4 -9
  20. package/dist/src/core/XPathHelper.js.map +1 -1
  21. package/dist/src/core/XmlParser.d.ts.map +1 -1
  22. package/dist/src/core/XmlParser.js.map +1 -1
  23. package/dist/src/core/YamlParser.d.ts.map +1 -1
  24. package/dist/src/core/YamlParser.js.map +1 -1
  25. package/dist/src/core/errors.js.map +1 -1
  26. package/dist/src/engine/LintEngine.d.ts.map +1 -1
  27. package/dist/src/engine/LintEngine.js +2 -4
  28. package/dist/src/engine/LintEngine.js.map +1 -1
  29. package/dist/src/formatter/MuleXmlFormatter.d.ts.map +1 -1
  30. package/dist/src/formatter/MuleXmlFormatter.js +7 -1
  31. package/dist/src/formatter/MuleXmlFormatter.js.map +1 -1
  32. package/dist/src/formatter/index.d.ts +2 -2
  33. package/dist/src/formatter/index.d.ts.map +1 -1
  34. package/dist/src/formatter/index.js.map +1 -1
  35. package/dist/src/formatters/CsvFormatter.js.map +1 -1
  36. package/dist/src/formatters/HtmlFormatter.js +0 -1
  37. package/dist/src/formatters/HtmlFormatter.js.map +1 -1
  38. package/dist/src/formatters/JsonFormatter.d.ts.map +1 -1
  39. package/dist/src/formatters/JsonFormatter.js.map +1 -1
  40. package/dist/src/formatters/SarifFormatter.js.map +1 -1
  41. package/dist/src/formatters/TableFormatter.js.map +1 -1
  42. package/dist/src/formatters/html/components/Icons.js +1 -1
  43. package/dist/src/formatters/html/components/Icons.js.map +1 -1
  44. package/dist/src/formatters/html/components/MetricCard.d.ts.map +1 -1
  45. package/dist/src/formatters/html/components/MetricCard.js.map +1 -1
  46. package/dist/src/formatters/html/components/Modal.d.ts.map +1 -1
  47. package/dist/src/formatters/html/components/Modal.js.map +1 -1
  48. package/dist/src/formatters/html/components/RatingBadge.d.ts.map +1 -1
  49. package/dist/src/formatters/html/components/RatingBadge.js +6 -6
  50. package/dist/src/formatters/html/components/RatingBadge.js.map +1 -1
  51. package/dist/src/formatters/html/index.d.ts +2 -2
  52. package/dist/src/formatters/html/index.d.ts.map +1 -1
  53. package/dist/src/formatters/html/index.js.map +1 -1
  54. package/dist/src/formatters/html/scripts/charts.d.ts.map +1 -1
  55. package/dist/src/formatters/html/scripts/charts.js +45 -12
  56. package/dist/src/formatters/html/scripts/charts.js.map +1 -1
  57. package/dist/src/formatters/html/scripts/index.d.ts.map +1 -1
  58. package/dist/src/formatters/html/scripts/index.js.map +1 -1
  59. package/dist/src/formatters/html/scripts/renderer.d.ts.map +1 -1
  60. package/dist/src/formatters/html/scripts/renderer.js +175 -35
  61. package/dist/src/formatters/html/scripts/renderer.js.map +1 -1
  62. package/dist/src/formatters/html/sections/Header.d.ts.map +1 -1
  63. package/dist/src/formatters/html/sections/Header.js.map +1 -1
  64. package/dist/src/formatters/html/sections/LintSummary.d.ts.map +1 -1
  65. package/dist/src/formatters/html/sections/LintSummary.js.map +1 -1
  66. package/dist/src/formatters/html/sections/QualityRatings.js.map +1 -1
  67. package/dist/src/formatters/html/sections/Sidebar.d.ts.map +1 -1
  68. package/dist/src/formatters/html/sections/Sidebar.js.map +1 -1
  69. package/dist/src/formatters/html/styles/badges.d.ts.map +1 -1
  70. package/dist/src/formatters/html/styles/badges.js.map +1 -1
  71. package/dist/src/formatters/html/styles/index.d.ts.map +1 -1
  72. package/dist/src/formatters/html/styles/index.js.map +1 -1
  73. package/dist/src/formatters/html/theme.js.map +1 -1
  74. package/dist/src/formatters/html/views/Dashboard.js +5 -5
  75. package/dist/src/formatters/html/views/Dashboard.js.map +1 -1
  76. package/dist/src/formatters/html/views/IssuesView.d.ts.map +1 -1
  77. package/dist/src/formatters/html/views/IssuesView.js.map +1 -1
  78. package/dist/src/formatters/index.js.map +1 -1
  79. package/dist/src/mcp/index.d.ts.map +1 -1
  80. package/dist/src/mcp/index.js +12 -511
  81. package/dist/src/mcp/index.js.map +1 -1
  82. package/dist/src/mcp/prompts/index.d.ts +6 -0
  83. package/dist/src/mcp/prompts/index.d.ts.map +1 -0
  84. package/dist/src/mcp/prompts/index.js +87 -0
  85. package/dist/src/mcp/prompts/index.js.map +1 -0
  86. package/dist/src/mcp/resources/index.d.ts +6 -0
  87. package/dist/src/mcp/resources/index.d.ts.map +1 -0
  88. package/dist/src/mcp/resources/index.js +189 -0
  89. package/dist/src/mcp/resources/index.js.map +1 -0
  90. package/dist/src/mcp/tools/formatMuleXml.d.ts +6 -0
  91. package/dist/src/mcp/tools/formatMuleXml.d.ts.map +1 -0
  92. package/dist/src/mcp/tools/formatMuleXml.js +133 -0
  93. package/dist/src/mcp/tools/formatMuleXml.js.map +1 -0
  94. package/dist/src/mcp/tools/getRuleDetails.d.ts +6 -0
  95. package/dist/src/mcp/tools/getRuleDetails.d.ts.map +1 -0
  96. package/dist/src/mcp/tools/getRuleDetails.js +44 -0
  97. package/dist/src/mcp/tools/getRuleDetails.js.map +1 -0
  98. package/dist/src/mcp/tools/runLintAnalysis.d.ts +7 -0
  99. package/dist/src/mcp/tools/runLintAnalysis.d.ts.map +1 -0
  100. package/dist/src/mcp/tools/runLintAnalysis.js +76 -0
  101. package/dist/src/mcp/tools/runLintAnalysis.js.map +1 -0
  102. package/dist/src/mcp/tools/validateSnippet.d.ts +6 -0
  103. package/dist/src/mcp/tools/validateSnippet.d.ts.map +1 -0
  104. package/dist/src/mcp/tools/validateSnippet.js +59 -0
  105. package/dist/src/mcp/tools/validateSnippet.js.map +1 -0
  106. package/dist/src/quality/calculator.d.ts.map +1 -1
  107. package/dist/src/quality/calculator.js +1 -1
  108. package/dist/src/quality/calculator.js.map +1 -1
  109. package/dist/src/quality/index.d.ts.map +1 -1
  110. package/dist/src/quality/index.js.map +1 -1
  111. package/dist/src/quality/thresholds.js.map +1 -1
  112. package/dist/src/quality/types.d.ts.map +1 -1
  113. package/dist/src/rules/api-led/ApiLedRules.d.ts.map +1 -1
  114. package/dist/src/rules/api-led/ApiLedRules.js +6 -2
  115. package/dist/src/rules/api-led/ApiLedRules.js.map +1 -1
  116. package/dist/src/rules/api-led/SingleSystemSapiRule.d.ts.map +1 -1
  117. package/dist/src/rules/api-led/SingleSystemSapiRule.js.map +1 -1
  118. package/dist/src/rules/base/BaseRule.d.ts.map +1 -1
  119. package/dist/src/rules/base/BaseRule.js.map +1 -1
  120. package/dist/src/rules/base/ProjectRule.d.ts.map +1 -1
  121. package/dist/src/rules/base/ProjectRule.js.map +1 -1
  122. package/dist/src/rules/complexity/FlowComplexityRule.d.ts.map +1 -1
  123. package/dist/src/rules/complexity/FlowComplexityRule.js.map +1 -1
  124. package/dist/src/rules/dataweave/DataWeaveRules.d.ts.map +1 -1
  125. package/dist/src/rules/dataweave/DataWeaveRules.js +1 -3
  126. package/dist/src/rules/dataweave/DataWeaveRules.js.map +1 -1
  127. package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.d.ts.map +1 -1
  128. package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.js.map +1 -1
  129. package/dist/src/rules/documentation/DisplayNameRule.d.ts.map +1 -1
  130. package/dist/src/rules/documentation/DisplayNameRule.js.map +1 -1
  131. package/dist/src/rules/documentation/FlowDescriptionRule.d.ts.map +1 -1
  132. package/dist/src/rules/documentation/FlowDescriptionRule.js.map +1 -1
  133. package/dist/src/rules/documentation/MissingDocNameRule.d.ts.map +1 -1
  134. package/dist/src/rules/documentation/MissingDocNameRule.js.map +1 -1
  135. package/dist/src/rules/error-handling/CorrelationIdRule.d.ts.map +1 -1
  136. package/dist/src/rules/error-handling/CorrelationIdRule.js.map +1 -1
  137. package/dist/src/rules/error-handling/GenericErrorRule.d.ts.map +1 -1
  138. package/dist/src/rules/error-handling/GenericErrorRule.js.map +1 -1
  139. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts.map +1 -1
  140. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js.map +1 -1
  141. package/dist/src/rules/error-handling/HttpStatusRule.d.ts.map +1 -1
  142. package/dist/src/rules/error-handling/HttpStatusRule.js.map +1 -1
  143. package/dist/src/rules/error-handling/MissingErrorHandlerRule.d.ts.map +1 -1
  144. package/dist/src/rules/error-handling/MissingErrorHandlerRule.js.map +1 -1
  145. package/dist/src/rules/error-handling/TryScopeRule.d.ts.map +1 -1
  146. package/dist/src/rules/error-handling/TryScopeRule.js.map +1 -1
  147. package/dist/src/rules/experimental/ExperimentalRules.d.ts.map +1 -1
  148. package/dist/src/rules/experimental/ExperimentalRules.js +6 -2
  149. package/dist/src/rules/experimental/ExperimentalRules.js.map +1 -1
  150. package/dist/src/rules/governance/GovernanceRules.d.ts +2 -2
  151. package/dist/src/rules/governance/GovernanceRules.d.ts.map +1 -1
  152. package/dist/src/rules/governance/GovernanceRules.js +2 -2
  153. package/dist/src/rules/governance/GovernanceRules.js.map +1 -1
  154. package/dist/src/rules/http/HttpContentTypeRule.d.ts.map +1 -1
  155. package/dist/src/rules/http/HttpContentTypeRule.js.map +1 -1
  156. package/dist/src/rules/http/HttpTimeoutRule.d.ts.map +1 -1
  157. package/dist/src/rules/http/HttpTimeoutRule.js.map +1 -1
  158. package/dist/src/rules/http/HttpUserAgentRule.d.ts.map +1 -1
  159. package/dist/src/rules/http/HttpUserAgentRule.js.map +1 -1
  160. package/dist/src/rules/index.js.map +1 -1
  161. package/dist/src/rules/logging/ExcessiveLoggersRule.d.ts.map +1 -1
  162. package/dist/src/rules/logging/ExcessiveLoggersRule.js.map +1 -1
  163. package/dist/src/rules/logging/LoggerCategoryRule.d.ts.map +1 -1
  164. package/dist/src/rules/logging/LoggerCategoryRule.js.map +1 -1
  165. package/dist/src/rules/logging/LoggerInUntilSuccessfulRule.d.ts.map +1 -1
  166. package/dist/src/rules/logging/LoggerInUntilSuccessfulRule.js.map +1 -1
  167. package/dist/src/rules/logging/LoggerPayloadRule.d.ts.map +1 -1
  168. package/dist/src/rules/logging/LoggerPayloadRule.js.map +1 -1
  169. package/dist/src/rules/logging/NewLoggingRules.d.ts.map +1 -1
  170. package/dist/src/rules/logging/NewLoggingRules.js.map +1 -1
  171. package/dist/src/rules/naming/FlowCasingRule.d.ts.map +1 -1
  172. package/dist/src/rules/naming/FlowCasingRule.js.map +1 -1
  173. package/dist/src/rules/naming/FlowNamingRule.d.ts.map +1 -1
  174. package/dist/src/rules/naming/FlowNamingRule.js +3 -1
  175. package/dist/src/rules/naming/FlowNamingRule.js.map +1 -1
  176. package/dist/src/rules/naming/VariableNamingRule.d.ts.map +1 -1
  177. package/dist/src/rules/naming/VariableNamingRule.js.map +1 -1
  178. package/dist/src/rules/operations/CommentedCodeRule.d.ts.map +1 -1
  179. package/dist/src/rules/operations/CommentedCodeRule.js.map +1 -1
  180. package/dist/src/rules/operations/UnusedFlowRule.d.ts.map +1 -1
  181. package/dist/src/rules/operations/UnusedFlowRule.js.map +1 -1
  182. package/dist/src/rules/performance/AsyncErrorHandlerRule.d.ts.map +1 -1
  183. package/dist/src/rules/performance/AsyncErrorHandlerRule.js.map +1 -1
  184. package/dist/src/rules/performance/ConnectionPoolingRule.d.ts.map +1 -1
  185. package/dist/src/rules/performance/ConnectionPoolingRule.js.map +1 -1
  186. package/dist/src/rules/performance/LargeChoiceBlockRule.d.ts.map +1 -1
  187. package/dist/src/rules/performance/LargeChoiceBlockRule.js.map +1 -1
  188. package/dist/src/rules/performance/ReconnectionStrategyRule.d.ts.map +1 -1
  189. package/dist/src/rules/performance/ReconnectionStrategyRule.js.map +1 -1
  190. package/dist/src/rules/performance/ScatterGatherRoutesRule.d.ts.map +1 -1
  191. package/dist/src/rules/performance/ScatterGatherRoutesRule.js.map +1 -1
  192. package/dist/src/rules/security/EncryptionKeyInLogsRule.d.ts.map +1 -1
  193. package/dist/src/rules/security/EncryptionKeyInLogsRule.js.map +1 -1
  194. package/dist/src/rules/security/HardcodedCredentialsRule.d.ts.map +1 -1
  195. package/dist/src/rules/security/HardcodedCredentialsRule.js.map +1 -1
  196. package/dist/src/rules/security/HardcodedHttpRule.d.ts.map +1 -1
  197. package/dist/src/rules/security/HardcodedHttpRule.js +1 -9
  198. package/dist/src/rules/security/HardcodedHttpRule.js.map +1 -1
  199. package/dist/src/rules/security/InputValidationRule.d.ts.map +1 -1
  200. package/dist/src/rules/security/InputValidationRule.js +1 -3
  201. package/dist/src/rules/security/InputValidationRule.js.map +1 -1
  202. package/dist/src/rules/security/InsecureTlsRule.d.ts.map +1 -1
  203. package/dist/src/rules/security/InsecureTlsRule.js.map +1 -1
  204. package/dist/src/rules/security/RateLimitingRule.d.ts.map +1 -1
  205. package/dist/src/rules/security/RateLimitingRule.js.map +1 -1
  206. package/dist/src/rules/security/TlsVersionRule.d.ts.map +1 -1
  207. package/dist/src/rules/security/TlsVersionRule.js.map +1 -1
  208. package/dist/src/rules/standards/ApiKitValidationRule.d.ts.map +1 -1
  209. package/dist/src/rules/standards/ApiKitValidationRule.js.map +1 -1
  210. package/dist/src/rules/standards/AutoDiscoveryRule.d.ts.map +1 -1
  211. package/dist/src/rules/standards/AutoDiscoveryRule.js.map +1 -1
  212. package/dist/src/rules/standards/ChoiceAntiPatternRule.d.ts.map +1 -1
  213. package/dist/src/rules/standards/ChoiceAntiPatternRule.js.map +1 -1
  214. package/dist/src/rules/standards/CronExternalizedRule.d.ts.map +1 -1
  215. package/dist/src/rules/standards/CronExternalizedRule.js.map +1 -1
  216. package/dist/src/rules/standards/DeprecatedComponentRule.d.ts.map +1 -1
  217. package/dist/src/rules/standards/DeprecatedComponentRule.js.map +1 -1
  218. package/dist/src/rules/standards/DwlStandardsRule.d.ts.map +1 -1
  219. package/dist/src/rules/standards/DwlStandardsRule.js.map +1 -1
  220. package/dist/src/rules/standards/HttpPortPlaceholderRule.d.ts.map +1 -1
  221. package/dist/src/rules/standards/HttpPortPlaceholderRule.js.map +1 -1
  222. package/dist/src/rules/structure/StructureRules.d.ts.map +1 -1
  223. package/dist/src/rules/structure/StructureRules.js.map +1 -1
  224. package/dist/src/rules/yaml/YamlRules.d.ts.map +1 -1
  225. package/dist/src/rules/yaml/YamlRules.js +2 -7
  226. package/dist/src/rules/yaml/YamlRules.js.map +1 -1
  227. package/dist/src/types/Config.d.ts.map +1 -1
  228. package/dist/src/types/Config.js.map +1 -1
  229. package/dist/src/types/QualityGate.d.ts.map +1 -1
  230. package/dist/src/types/QualityGate.js.map +1 -1
  231. package/dist/src/types/Report.d.ts.map +1 -1
  232. package/dist/src/types/Rule.d.ts +1 -1
  233. package/dist/src/types/Rule.d.ts.map +1 -1
  234. package/docs/README.md +27 -27
  235. package/docs/best-practices/documentation-standards.md +20 -11
  236. package/docs/best-practices/folder-structure.md +16 -10
  237. package/docs/best-practices/mulesoft-best-practices.md +96 -94
  238. package/docs/best-practices/rules-catalog.md +316 -287
  239. package/docs/linter/architecture.md +70 -64
  240. package/docs/linter/extending.md +137 -128
  241. package/docs/linter/folder-structure.md +39 -38
  242. package/docs/linter/naming-conventions.md +80 -78
  243. package/docs/linter/rule-engine.md +306 -306
  244. package/docs/mcp-design.md +35 -21
  245. package/package.json +84 -73
@@ -27,62 +27,62 @@ import { Document } from '@xmldom/xmldom';
27
27
 
28
28
  export type Severity = 'error' | 'warning' | 'info';
29
29
 
30
- export type RuleCategory =
31
- | 'naming'
32
- | 'error-handling'
33
- | 'security'
34
- | 'logging'
35
- | 'standards'
36
- | 'performance'
37
- | 'documentation';
30
+ export type RuleCategory =
31
+ | 'naming'
32
+ | 'error-handling'
33
+ | 'security'
34
+ | 'logging'
35
+ | 'standards'
36
+ | 'performance'
37
+ | 'documentation';
38
38
 
39
39
  // Issue type for quality metrics classification
40
40
  export type IssueType = 'code-smell' | 'bug' | 'vulnerability';
41
41
 
42
42
  export interface Issue {
43
- line: number;
44
- column?: number;
45
- message: string;
46
- ruleId: string;
47
- severity: Severity;
48
- suggestion?: string; // Optional fix suggestion
49
- codeSnippet?: string; // Relevant code context
43
+ line: number;
44
+ column?: number;
45
+ message: string;
46
+ ruleId: string;
47
+ severity: Severity;
48
+ suggestion?: string; // Optional fix suggestion
49
+ codeSnippet?: string; // Relevant code context
50
50
  }
51
51
 
52
52
  export interface ValidationContext {
53
- filePath: string; // Absolute path to file
54
- relativePath: string; // Path relative to project root
55
- projectRoot: string; // Project root directory
56
- config: RuleConfig; // Rule-specific configuration
53
+ filePath: string; // Absolute path to file
54
+ relativePath: string; // Path relative to project root
55
+ projectRoot: string; // Project root directory
56
+ config: RuleConfig; // Rule-specific configuration
57
57
  }
58
58
 
59
59
  export interface Rule {
60
- // Unique identifier (e.g., "MULE-001")
61
- id: string;
62
-
63
- // Human-readable name
64
- name: string;
65
-
66
- // Detailed description for documentation
67
- description: string;
68
-
69
- // Default severity (can be overridden by config)
70
- severity: Severity;
71
-
72
- // Category for grouping in reports
73
- category: RuleCategory;
74
-
75
- // Issue type for quality metrics (defaults to 'code-smell')
76
- // - 'bug': Reliability issues (error-handling rules)
77
- // - 'vulnerability': Security issues (security rules)
78
- // - 'code-smell': Maintainability issues (default)
79
- issueType: IssueType;
80
-
81
- // Optional: documentation URL
82
- docsUrl?: string;
83
-
84
- // The validation function
85
- validate(doc: Document, context: ValidationContext): Issue[];
60
+ // Unique identifier (e.g., "MULE-001")
61
+ id: string;
62
+
63
+ // Human-readable name
64
+ name: string;
65
+
66
+ // Detailed description for documentation
67
+ description: string;
68
+
69
+ // Default severity (can be overridden by config)
70
+ severity: Severity;
71
+
72
+ // Category for grouping in reports
73
+ category: RuleCategory;
74
+
75
+ // Issue type for quality metrics (defaults to 'code-smell')
76
+ // - 'bug': Reliability issues (error-handling rules)
77
+ // - 'vulnerability': Security issues (security rules)
78
+ // - 'code-smell': Maintainability issues (default)
79
+ issueType: IssueType;
80
+
81
+ // Optional: documentation URL
82
+ docsUrl?: string;
83
+
84
+ // The validation function
85
+ validate(doc: Document, context: ValidationContext): Issue[];
86
86
  }
87
87
  ```
88
88
 
@@ -92,9 +92,9 @@ Per-rule configuration:
92
92
 
93
93
  ```typescript
94
94
  export interface RuleConfig {
95
- enabled: boolean;
96
- severity?: Severity; // Override default
97
- options?: Record<string, unknown>; // Rule-specific options
95
+ enabled: boolean;
96
+ severity?: Severity; // Override default
97
+ options?: Record<string, unknown>; // Rule-specific options
98
98
  }
99
99
  ```
100
100
 
@@ -110,78 +110,78 @@ import { Rule, Issue, Severity, RuleCategory, ValidationContext } from '@types';
110
110
  import { XPathHelper } from '@core/XPathHelper';
111
111
 
112
112
  export abstract class BaseRule implements Rule {
113
- abstract id: string;
114
- abstract name: string;
115
- abstract description: string;
116
- abstract severity: Severity;
117
- abstract category: RuleCategory;
118
-
119
- docsUrl?: string;
120
-
121
- protected xpath = XPathHelper.getInstance();
122
-
123
- abstract validate(doc: Document, context: ValidationContext): Issue[];
124
-
125
- // --- Utility Methods ---
126
-
127
- /**
128
- * Execute XPath and return matching nodes
129
- */
130
- protected select(expression: string, doc: Document): Node[] {
131
- return this.xpath.select(expression, doc);
132
- }
133
-
134
- /**
135
- * Create an issue with consistent formatting
136
- */
137
- protected createIssue(
138
- node: Node,
139
- message: string,
140
- options?: {
141
- suggestion?: string;
142
- severity?: Severity;
143
- }
144
- ): Issue {
145
- return {
146
- line: this.getLineNumber(node),
147
- column: this.getColumnNumber(node),
148
- message,
149
- ruleId: this.id,
150
- severity: options?.severity ?? this.severity,
151
- suggestion: options?.suggestion,
152
- };
153
- }
154
-
155
- /**
156
- * Get line number from node (xmldom stores this)
157
- */
158
- protected getLineNumber(node: Node): number {
159
- // xmldom stores line info in columnNumber/lineNumber
160
- return (node as any).lineNumber ?? 1;
161
- }
162
-
163
- /**
164
- * Get column number from node
165
- */
166
- protected getColumnNumber(node: Node): number | undefined {
167
- return (node as any).columnNumber;
168
- }
169
-
170
- /**
171
- * Check if a node has a specific attribute
172
- */
173
- protected hasAttribute(node: Node, attrName: string): boolean {
174
- const element = node as Element;
175
- return element.hasAttribute?.(attrName) ?? false;
176
- }
177
-
178
- /**
179
- * Get attribute value from node
180
- */
181
- protected getAttribute(node: Node, attrName: string): string | null {
182
- const element = node as Element;
183
- return element.getAttribute?.(attrName) ?? null;
184
- }
113
+ abstract id: string;
114
+ abstract name: string;
115
+ abstract description: string;
116
+ abstract severity: Severity;
117
+ abstract category: RuleCategory;
118
+
119
+ docsUrl?: string;
120
+
121
+ protected xpath = XPathHelper.getInstance();
122
+
123
+ abstract validate(doc: Document, context: ValidationContext): Issue[];
124
+
125
+ // --- Utility Methods ---
126
+
127
+ /**
128
+ * Execute XPath and return matching nodes
129
+ */
130
+ protected select(expression: string, doc: Document): Node[] {
131
+ return this.xpath.select(expression, doc);
132
+ }
133
+
134
+ /**
135
+ * Create an issue with consistent formatting
136
+ */
137
+ protected createIssue(
138
+ node: Node,
139
+ message: string,
140
+ options?: {
141
+ suggestion?: string;
142
+ severity?: Severity;
143
+ },
144
+ ): Issue {
145
+ return {
146
+ line: this.getLineNumber(node),
147
+ column: this.getColumnNumber(node),
148
+ message,
149
+ ruleId: this.id,
150
+ severity: options?.severity ?? this.severity,
151
+ suggestion: options?.suggestion,
152
+ };
153
+ }
154
+
155
+ /**
156
+ * Get line number from node (xmldom stores this)
157
+ */
158
+ protected getLineNumber(node: Node): number {
159
+ // xmldom stores line info in columnNumber/lineNumber
160
+ return (node as any).lineNumber ?? 1;
161
+ }
162
+
163
+ /**
164
+ * Get column number from node
165
+ */
166
+ protected getColumnNumber(node: Node): number | undefined {
167
+ return (node as any).columnNumber;
168
+ }
169
+
170
+ /**
171
+ * Check if a node has a specific attribute
172
+ */
173
+ protected hasAttribute(node: Node, attrName: string): boolean {
174
+ const element = node as Element;
175
+ return element.hasAttribute?.(attrName) ?? false;
176
+ }
177
+
178
+ /**
179
+ * Get attribute value from node
180
+ */
181
+ protected getAttribute(node: Node, attrName: string): string | null {
182
+ const element = node as Element;
183
+ return element.getAttribute?.(attrName) ?? null;
184
+ }
185
185
  }
186
186
  ```
187
187
 
@@ -193,43 +193,43 @@ export abstract class BaseRule implements Rule {
193
193
 
194
194
  ```typescript
195
195
  const MULE_NAMESPACES = {
196
- 'mule': 'http://www.mulesoft.org/schema/mule/core',
197
- 'http': 'http://www.mulesoft.org/schema/mule/http',
198
- 'https': 'http://www.mulesoft.org/schema/mule/https',
199
- 'ee': 'http://www.mulesoft.org/schema/mule/ee/core',
200
- 'doc': 'http://www.mulesoft.org/schema/mule/documentation',
201
- 'tls': 'http://www.mulesoft.org/schema/mule/tls',
202
- 'db': 'http://www.mulesoft.org/schema/mule/db',
203
- 'file': 'http://www.mulesoft.org/schema/mule/file',
204
- 'sftp': 'http://www.mulesoft.org/schema/mule/sftp',
205
- 'vm': 'http://www.mulesoft.org/schema/mule/vm',
206
- 'jms': 'http://www.mulesoft.org/schema/mule/jms',
207
- 'apikit': 'http://www.mulesoft.org/schema/mule/mule-apikit',
208
- 'api-gateway': 'http://www.mulesoft.org/schema/mule/api-gateway',
209
- 'secure-properties': 'http://www.mulesoft.org/schema/mule/secure-properties',
210
- 'os': 'http://www.mulesoft.org/schema/mule/os',
211
- 'batch': 'http://www.mulesoft.org/schema/mule/batch',
196
+ mule: 'http://www.mulesoft.org/schema/mule/core',
197
+ http: 'http://www.mulesoft.org/schema/mule/http',
198
+ https: 'http://www.mulesoft.org/schema/mule/https',
199
+ ee: 'http://www.mulesoft.org/schema/mule/ee/core',
200
+ doc: 'http://www.mulesoft.org/schema/mule/documentation',
201
+ tls: 'http://www.mulesoft.org/schema/mule/tls',
202
+ db: 'http://www.mulesoft.org/schema/mule/db',
203
+ file: 'http://www.mulesoft.org/schema/mule/file',
204
+ sftp: 'http://www.mulesoft.org/schema/mule/sftp',
205
+ vm: 'http://www.mulesoft.org/schema/mule/vm',
206
+ jms: 'http://www.mulesoft.org/schema/mule/jms',
207
+ apikit: 'http://www.mulesoft.org/schema/mule/mule-apikit',
208
+ 'api-gateway': 'http://www.mulesoft.org/schema/mule/api-gateway',
209
+ 'secure-properties': 'http://www.mulesoft.org/schema/mule/secure-properties',
210
+ os: 'http://www.mulesoft.org/schema/mule/os',
211
+ batch: 'http://www.mulesoft.org/schema/mule/batch',
212
212
  };
213
213
  ```
214
214
 
215
215
  ### Common XPath Patterns
216
216
 
217
- | Purpose | XPath Expression |
218
- |---------|------------------|
219
- | All flows | `//mule:flow` |
220
- | All sub-flows | `//mule:sub-flow` |
221
- | Flow by name | `//mule:flow[@name='my-flow']` |
222
- | All loggers | `//mule:logger` |
223
- | Loggers without category | `//mule:logger[not(@category)]` |
224
- | All error handlers | `//mule:error-handler` |
225
- | All on-error blocks | `//mule:on-error-continue \| //mule:on-error-propagate` |
226
- | All HTTP listeners | `//http:listener` |
227
- | All HTTP requests | `//http:request` |
228
- | Choice blocks | `//mule:choice` |
229
- | DataWeave transforms | `//ee:transform` |
230
- | Set-variable | `//mule:set-variable` |
231
- | Flows without error handler | `//mule:flow[not(mule:error-handler)]` |
232
- | Attributes starting with http | `//*[@*[starts-with(., 'http:')]]` |
217
+ | Purpose | XPath Expression |
218
+ | ----------------------------- | ------------------------------------------------------- |
219
+ | All flows | `//mule:flow` |
220
+ | All sub-flows | `//mule:sub-flow` |
221
+ | Flow by name | `//mule:flow[@name='my-flow']` |
222
+ | All loggers | `//mule:logger` |
223
+ | Loggers without category | `//mule:logger[not(@category)]` |
224
+ | All error handlers | `//mule:error-handler` |
225
+ | All on-error blocks | `//mule:on-error-continue \| //mule:on-error-propagate` |
226
+ | All HTTP listeners | `//http:listener` |
227
+ | All HTTP requests | `//http:request` |
228
+ | Choice blocks | `//mule:choice` |
229
+ | DataWeave transforms | `//ee:transform` |
230
+ | Set-variable | `//mule:set-variable` |
231
+ | Flows without error handler | `//mule:flow[not(mule:error-handler)]` |
232
+ | Attributes starting with http | `//*[@*[starts-with(., 'http:')]]` |
233
233
 
234
234
  ---
235
235
 
@@ -242,43 +242,43 @@ import { Document, Node } from '@xmldom/xmldom';
242
242
  import { BaseRule, Issue, ValidationContext } from '@types';
243
243
 
244
244
  export class FlowNamingRule extends BaseRule {
245
- id = 'MULE-002';
246
- name = 'Flow Naming Convention';
247
- description = 'Flows must end with "-flow", sub-flows with "-subflow"';
248
- severity = 'warning' as const;
249
- category = 'naming' as const;
250
-
251
- validate(doc: Document, context: ValidationContext): Issue[] {
252
- const issues: Issue[] = [];
253
-
254
- // Check flows
255
- const flows = this.select('//mule:flow', doc);
256
- for (const flow of flows) {
257
- const name = this.getAttribute(flow, 'name');
258
- if (name && !name.endsWith('-flow')) {
259
- issues.push(this.createIssue(
260
- flow,
261
- `Flow "${name}" should end with "-flow"`,
262
- { suggestion: `Rename to "${name}-flow"` }
263
- ));
264
- }
265
- }
266
-
267
- // Check sub-flows
268
- const subflows = this.select('//mule:sub-flow', doc);
269
- for (const subflow of subflows) {
270
- const name = this.getAttribute(subflow, 'name');
271
- if (name && !name.endsWith('-subflow')) {
272
- issues.push(this.createIssue(
273
- subflow,
274
- `Sub-flow "${name}" should end with "-subflow"`,
275
- { suggestion: `Rename to "${name}-subflow"` }
276
- ));
277
- }
278
- }
279
-
280
- return issues;
245
+ id = 'MULE-002';
246
+ name = 'Flow Naming Convention';
247
+ description = 'Flows must end with "-flow", sub-flows with "-subflow"';
248
+ severity = 'warning' as const;
249
+ category = 'naming' as const;
250
+
251
+ validate(doc: Document, context: ValidationContext): Issue[] {
252
+ const issues: Issue[] = [];
253
+
254
+ // Check flows
255
+ const flows = this.select('//mule:flow', doc);
256
+ for (const flow of flows) {
257
+ const name = this.getAttribute(flow, 'name');
258
+ if (name && !name.endsWith('-flow')) {
259
+ issues.push(
260
+ this.createIssue(flow, `Flow "${name}" should end with "-flow"`, {
261
+ suggestion: `Rename to "${name}-flow"`,
262
+ }),
263
+ );
264
+ }
265
+ }
266
+
267
+ // Check sub-flows
268
+ const subflows = this.select('//mule:sub-flow', doc);
269
+ for (const subflow of subflows) {
270
+ const name = this.getAttribute(subflow, 'name');
271
+ if (name && !name.endsWith('-subflow')) {
272
+ issues.push(
273
+ this.createIssue(subflow, `Sub-flow "${name}" should end with "-subflow"`, {
274
+ suggestion: `Rename to "${name}-subflow"`,
275
+ }),
276
+ );
277
+ }
281
278
  }
279
+
280
+ return issues;
281
+ }
282
282
  }
283
283
  ```
284
284
 
@@ -289,30 +289,28 @@ import { Document, Node } from '@xmldom/xmldom';
289
289
  import { BaseRule, Issue, ValidationContext } from '@types';
290
290
 
291
291
  export class LoggerCategoryRule extends BaseRule {
292
- id = 'MULE-006';
293
- name = 'Logger Category Required';
294
- description = 'All loggers must have a category attribute for proper log filtering';
295
- severity = 'warning' as const;
296
- category = 'logging' as const;
297
-
298
- validate(doc: Document, context: ValidationContext): Issue[] {
299
- const issues: Issue[] = [];
300
-
301
- const loggers = this.select('//mule:logger[not(@category)]', doc);
302
-
303
- for (const logger of loggers) {
304
- const message = this.getAttribute(logger, 'message') || 'unknown';
305
- issues.push(this.createIssue(
306
- logger,
307
- `Logger is missing 'category' attribute`,
308
- {
309
- suggestion: `Add category="com.myorg.${context.relativePath.replace(/\//g, '.')}"`
310
- }
311
- ));
312
- }
313
-
314
- return issues;
292
+ id = 'MULE-006';
293
+ name = 'Logger Category Required';
294
+ description = 'All loggers must have a category attribute for proper log filtering';
295
+ severity = 'warning' as const;
296
+ category = 'logging' as const;
297
+
298
+ validate(doc: Document, context: ValidationContext): Issue[] {
299
+ const issues: Issue[] = [];
300
+
301
+ const loggers = this.select('//mule:logger[not(@category)]', doc);
302
+
303
+ for (const logger of loggers) {
304
+ const message = this.getAttribute(logger, 'message') || 'unknown';
305
+ issues.push(
306
+ this.createIssue(logger, `Logger is missing 'category' attribute`, {
307
+ suggestion: `Add category="com.myorg.${context.relativePath.replace(/\//g, '.')}"`,
308
+ }),
309
+ );
315
310
  }
311
+
312
+ return issues;
313
+ }
316
314
  }
317
315
  ```
318
316
 
@@ -323,54 +321,56 @@ import { Document, Node, Element } from '@xmldom/xmldom';
323
321
  import { BaseRule, Issue, ValidationContext } from '@types';
324
322
 
325
323
  export class HardcodedHttpRule extends BaseRule {
326
- id = 'MULE-004';
327
- name = 'Hardcoded HTTP URLs';
328
- description = 'HTTP/HTTPS URLs should use properties, not hardcoded values';
329
- severity = 'error' as const;
330
- category = 'security' as const;
331
-
332
- private readonly URL_PATTERN = /^https?:\/\//i;
333
- private readonly ALLOWED_PATTERNS = [
334
- /\$\{[^}]+\}/, // Property placeholders ${...}
335
- /\#\[[^\]]+\]/, // DataWeave expressions #[...]
336
- ];
337
-
338
- validate(doc: Document, context: ValidationContext): Issue[] {
339
- const issues: Issue[] = [];
340
-
341
- // Find all elements with attributes containing http:// or https://
342
- const allElements = doc.getElementsByTagName('*');
343
-
344
- for (let i = 0; i < allElements.length; i++) {
345
- const element = allElements[i];
346
- const attrs = element.attributes;
347
-
348
- for (let j = 0; j < attrs.length; j++) {
349
- const attr = attrs[j];
350
- const value = attr.value;
351
-
352
- if (this.URL_PATTERN.test(value) && !this.isAllowedPattern(value)) {
353
- issues.push(this.createIssue(
354
- element,
355
- `Hardcoded URL "${this.truncate(value)}" found in attribute "${attr.name}"`,
356
- {
357
- suggestion: 'Use property placeholder: ${http.url}'
358
- }
359
- ));
360
- }
361
- }
324
+ id = 'MULE-004';
325
+ name = 'Hardcoded HTTP URLs';
326
+ description = 'HTTP/HTTPS URLs should use properties, not hardcoded values';
327
+ severity = 'error' as const;
328
+ category = 'security' as const;
329
+
330
+ private readonly URL_PATTERN = /^https?:\/\//i;
331
+ private readonly ALLOWED_PATTERNS = [
332
+ /\$\{[^}]+\}/, // Property placeholders ${...}
333
+ /\#\[[^\]]+\]/, // DataWeave expressions #[...]
334
+ ];
335
+
336
+ validate(doc: Document, context: ValidationContext): Issue[] {
337
+ const issues: Issue[] = [];
338
+
339
+ // Find all elements with attributes containing http:// or https://
340
+ const allElements = doc.getElementsByTagName('*');
341
+
342
+ for (let i = 0; i < allElements.length; i++) {
343
+ const element = allElements[i];
344
+ const attrs = element.attributes;
345
+
346
+ for (let j = 0; j < attrs.length; j++) {
347
+ const attr = attrs[j];
348
+ const value = attr.value;
349
+
350
+ if (this.URL_PATTERN.test(value) && !this.isAllowedPattern(value)) {
351
+ issues.push(
352
+ this.createIssue(
353
+ element,
354
+ `Hardcoded URL "${this.truncate(value)}" found in attribute "${attr.name}"`,
355
+ {
356
+ suggestion: 'Use property placeholder: ${http.url}',
357
+ },
358
+ ),
359
+ );
362
360
  }
363
-
364
- return issues;
365
- }
366
-
367
- private isAllowedPattern(value: string): boolean {
368
- return this.ALLOWED_PATTERNS.some(pattern => pattern.test(value));
369
- }
370
-
371
- private truncate(value: string, maxLen = 50): string {
372
- return value.length > maxLen ? value.substring(0, maxLen) + '...' : value;
361
+ }
373
362
  }
363
+
364
+ return issues;
365
+ }
366
+
367
+ private isAllowedPattern(value: string): boolean {
368
+ return this.ALLOWED_PATTERNS.some((pattern) => pattern.test(value));
369
+ }
370
+
371
+ private truncate(value: string, maxLen = 50): string {
372
+ return value.length > maxLen ? value.substring(0, maxLen) + '...' : value;
373
+ }
374
374
  }
375
375
  ```
376
376
 
@@ -397,16 +397,16 @@ import { DwlStandardsRule } from './standards/DwlStandardsRule';
397
397
 
398
398
  // Rule registry
399
399
  export const RULES: Rule[] = [
400
- new FlowNamingRule(),
401
- new GlobalErrorHandlerRule(),
402
- new MissingErrorHandlerRule(),
403
- new HardcodedHttpRule(),
404
- new HttpStatusRule(),
405
- new LoggerCategoryRule(),
406
- new CorrelationIdRule(),
407
- new ChoiceAntiPatternRule(),
408
- new GenericErrorRule(),
409
- new DwlStandardsRule(),
400
+ new FlowNamingRule(),
401
+ new GlobalErrorHandlerRule(),
402
+ new MissingErrorHandlerRule(),
403
+ new HardcodedHttpRule(),
404
+ new HttpStatusRule(),
405
+ new LoggerCategoryRule(),
406
+ new CorrelationIdRule(),
407
+ new ChoiceAntiPatternRule(),
408
+ new GenericErrorRule(),
409
+ new DwlStandardsRule(),
410
410
  ];
411
411
 
412
412
  // Export for external use
@@ -446,8 +446,8 @@ flowchart TD
446
446
  "rules": {
447
447
  "MULE-001": { "enabled": true, "severity": "error" },
448
448
  "MULE-002": { "enabled": true, "severity": "warning" },
449
- "MULE-003": {
450
- "enabled": true,
449
+ "MULE-003": {
450
+ "enabled": true,
451
451
  "options": {
452
452
  "excludePatterns": ["*-api-main"]
453
453
  }
@@ -466,12 +466,12 @@ flowchart TD
466
466
 
467
467
  Some rules support additional options:
468
468
 
469
- | Rule | Option | Type | Default | Description |
470
- |------|--------|------|---------|-------------|
471
- | MULE-002 | `flowSuffix` | string | `-flow` | Expected flow name suffix |
472
- | MULE-002 | `subflowSuffix` | string | `-subflow` | Expected sub-flow suffix |
473
- | MULE-003 | `excludePatterns` | string[] | `[]` | Flow name patterns to exclude |
474
- | MULE-006 | `requirePrefix` | string | `null` | Required category prefix |
469
+ | Rule | Option | Type | Default | Description |
470
+ | -------- | ----------------- | -------- | ---------- | ----------------------------- |
471
+ | MULE-002 | `flowSuffix` | string | `-flow` | Expected flow name suffix |
472
+ | MULE-002 | `subflowSuffix` | string | `-subflow` | Expected sub-flow suffix |
473
+ | MULE-003 | `excludePatterns` | string[] | `[]` | Flow name patterns to exclude |
474
+ | MULE-006 | `requirePrefix` | string | `null` | Required category prefix |
475
475
 
476
476
  ---
477
477
 
@@ -484,37 +484,37 @@ import { FlowNamingRule } from '../rules/naming/FlowNamingRule';
484
484
  import { parseXml } from '../core/XmlParser';
485
485
 
486
486
  describe('FlowNamingRule', () => {
487
- const rule = new FlowNamingRule();
488
-
489
- it('should pass for correctly named flow', () => {
490
- const xml = `
487
+ const rule = new FlowNamingRule();
488
+
489
+ it('should pass for correctly named flow', () => {
490
+ const xml = `
491
491
  <mule xmlns="http://www.mulesoft.org/schema/mule/core">
492
492
  <flow name="my-process-flow">
493
493
  <logger message="test"/>
494
494
  </flow>
495
495
  </mule>
496
496
  `;
497
- const doc = parseXml(xml);
498
- const issues = rule.validate(doc, mockContext);
499
-
500
- expect(issues).toHaveLength(0);
501
- });
502
-
503
- it('should fail for incorrectly named flow', () => {
504
- const xml = `
497
+ const doc = parseXml(xml);
498
+ const issues = rule.validate(doc, mockContext);
499
+
500
+ expect(issues).toHaveLength(0);
501
+ });
502
+
503
+ it('should fail for incorrectly named flow', () => {
504
+ const xml = `
505
505
  <mule xmlns="http://www.mulesoft.org/schema/mule/core">
506
506
  <flow name="myProcess">
507
507
  <logger message="test"/>
508
508
  </flow>
509
509
  </mule>
510
510
  `;
511
- const doc = parseXml(xml);
512
- const issues = rule.validate(doc, mockContext);
513
-
514
- expect(issues).toHaveLength(1);
515
- expect(issues[0].ruleId).toBe('MULE-002');
516
- expect(issues[0].message).toContain('myProcess');
517
- });
511
+ const doc = parseXml(xml);
512
+ const issues = rule.validate(doc, mockContext);
513
+
514
+ expect(issues).toHaveLength(1);
515
+ expect(issues[0].ruleId).toBe('MULE-002');
516
+ expect(issues[0].message).toContain('myProcess');
517
+ });
518
518
  });
519
519
  ```
520
520
 
@@ -522,20 +522,20 @@ describe('FlowNamingRule', () => {
522
522
 
523
523
  ## Performance Specifications
524
524
 
525
- | Metric | Target |
526
- |--------|--------|
527
- | Files per second | > 100 |
528
- | Memory per file | < 10MB |
529
- | Rule execution | < 50ms per rule |
530
- | Total for 100 files | < 5 seconds |
525
+ | Metric | Target |
526
+ | ------------------- | --------------- |
527
+ | Files per second | > 100 |
528
+ | Memory per file | < 10MB |
529
+ | Rule execution | < 50ms per rule |
530
+ | Total for 100 files | < 5 seconds |
531
531
 
532
532
  ---
533
533
 
534
534
  ## Exit Codes
535
535
 
536
- | Code | Meaning |
537
- |------|---------|
538
- | 0 | No errors or warnings |
539
- | 1 | At least one error found |
540
- | 2 | Configuration error |
541
- | 3 | Critical error (parse failure) |
536
+ | Code | Meaning |
537
+ | ---- | ------------------------------ |
538
+ | 0 | No errors or warnings |
539
+ | 1 | At least one error found |
540
+ | 2 | Configuration error |
541
+ | 3 | Critical error (parse failure) |