@sfdxy/mule-lint 1.19.0 → 1.21.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 (154) hide show
  1. package/README.md +128 -73
  2. package/dist/package.json +1 -1
  3. package/dist/src/core/XPathHelper.d.ts.map +1 -1
  4. package/dist/src/core/XPathHelper.js +8 -0
  5. package/dist/src/core/XPathHelper.js.map +1 -1
  6. package/dist/src/engine/LintEngine.d.ts +32 -0
  7. package/dist/src/engine/LintEngine.d.ts.map +1 -1
  8. package/dist/src/engine/LintEngine.js +166 -15
  9. package/dist/src/engine/LintEngine.js.map +1 -1
  10. package/dist/src/rules/api-led/ApikitConsoleProductionRule.d.ts +22 -0
  11. package/dist/src/rules/api-led/ApikitConsoleProductionRule.d.ts.map +1 -0
  12. package/dist/src/rules/api-led/ApikitConsoleProductionRule.js +43 -0
  13. package/dist/src/rules/api-led/ApikitConsoleProductionRule.js.map +1 -0
  14. package/dist/src/rules/api-led/ApikitMainFlowStructureRule.d.ts +24 -0
  15. package/dist/src/rules/api-led/ApikitMainFlowStructureRule.d.ts.map +1 -0
  16. package/dist/src/rules/api-led/ApikitMainFlowStructureRule.js +53 -0
  17. package/dist/src/rules/api-led/ApikitMainFlowStructureRule.js.map +1 -0
  18. package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.d.ts +25 -0
  19. package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.d.ts.map +1 -0
  20. package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.js +59 -0
  21. package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.js.map +1 -0
  22. package/dist/src/rules/connector/EventListenerNullGuardRule.d.ts +24 -0
  23. package/dist/src/rules/connector/EventListenerNullGuardRule.d.ts.map +1 -0
  24. package/dist/src/rules/connector/EventListenerNullGuardRule.js +58 -0
  25. package/dist/src/rules/connector/EventListenerNullGuardRule.js.map +1 -0
  26. package/dist/src/rules/connector/ReplayChannelConfigRule.d.ts +23 -0
  27. package/dist/src/rules/connector/ReplayChannelConfigRule.d.ts.map +1 -0
  28. package/dist/src/rules/connector/ReplayChannelConfigRule.js +52 -0
  29. package/dist/src/rules/connector/ReplayChannelConfigRule.js.map +1 -0
  30. package/dist/src/rules/dataweave/DataWeaveRules.d.ts +17 -4
  31. package/dist/src/rules/dataweave/DataWeaveRules.d.ts.map +1 -1
  32. package/dist/src/rules/dataweave/DataWeaveRules.js +36 -20
  33. package/dist/src/rules/dataweave/DataWeaveRules.js.map +1 -1
  34. package/dist/src/rules/dataweave/DuplicateTransformLogicRule.d.ts +25 -0
  35. package/dist/src/rules/dataweave/DuplicateTransformLogicRule.d.ts.map +1 -0
  36. package/dist/src/rules/dataweave/DuplicateTransformLogicRule.js +63 -0
  37. package/dist/src/rules/dataweave/DuplicateTransformLogicRule.js.map +1 -0
  38. package/dist/src/rules/error-handling/CatchAllLastRule.d.ts +24 -0
  39. package/dist/src/rules/error-handling/CatchAllLastRule.d.ts.map +1 -0
  40. package/dist/src/rules/error-handling/CatchAllLastRule.js +65 -0
  41. package/dist/src/rules/error-handling/CatchAllLastRule.js.map +1 -0
  42. package/dist/src/rules/error-handling/CorrelationIdRule.d.ts +22 -1
  43. package/dist/src/rules/error-handling/CorrelationIdRule.d.ts.map +1 -1
  44. package/dist/src/rules/error-handling/CorrelationIdRule.js +107 -6
  45. package/dist/src/rules/error-handling/CorrelationIdRule.js.map +1 -1
  46. package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.d.ts +28 -0
  47. package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.d.ts.map +1 -0
  48. package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.js +70 -0
  49. package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.js.map +1 -0
  50. package/dist/src/rules/error-handling/ErrorResponseStructureRule.d.ts +23 -0
  51. package/dist/src/rules/error-handling/ErrorResponseStructureRule.d.ts.map +1 -0
  52. package/dist/src/rules/error-handling/ErrorResponseStructureRule.js +73 -0
  53. package/dist/src/rules/error-handling/ErrorResponseStructureRule.js.map +1 -0
  54. package/dist/src/rules/error-handling/GenericErrorRule.d.ts +15 -3
  55. package/dist/src/rules/error-handling/GenericErrorRule.d.ts.map +1 -1
  56. package/dist/src/rules/error-handling/GenericErrorRule.js +58 -18
  57. package/dist/src/rules/error-handling/GenericErrorRule.js.map +1 -1
  58. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts +16 -5
  59. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts.map +1 -1
  60. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js +64 -21
  61. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js.map +1 -1
  62. package/dist/src/rules/error-handling/HttpStatusRule.d.ts +5 -0
  63. package/dist/src/rules/error-handling/HttpStatusRule.d.ts.map +1 -1
  64. package/dist/src/rules/error-handling/HttpStatusRule.js +15 -0
  65. package/dist/src/rules/error-handling/HttpStatusRule.js.map +1 -1
  66. package/dist/src/rules/error-handling/TryScopeRule.d.ts +5 -0
  67. package/dist/src/rules/error-handling/TryScopeRule.d.ts.map +1 -1
  68. package/dist/src/rules/error-handling/TryScopeRule.js +30 -7
  69. package/dist/src/rules/error-handling/TryScopeRule.js.map +1 -1
  70. package/dist/src/rules/http/ConnectionIdleTimeoutRule.d.ts +27 -0
  71. package/dist/src/rules/http/ConnectionIdleTimeoutRule.d.ts.map +1 -0
  72. package/dist/src/rules/http/ConnectionIdleTimeoutRule.js +46 -0
  73. package/dist/src/rules/http/ConnectionIdleTimeoutRule.js.map +1 -0
  74. package/dist/src/rules/http/HttpContentTypeRule.d.ts +28 -1
  75. package/dist/src/rules/http/HttpContentTypeRule.d.ts.map +1 -1
  76. package/dist/src/rules/http/HttpContentTypeRule.js +68 -7
  77. package/dist/src/rules/http/HttpContentTypeRule.js.map +1 -1
  78. package/dist/src/rules/index.d.ts +1 -1
  79. package/dist/src/rules/index.d.ts.map +1 -1
  80. package/dist/src/rules/index.js +50 -8
  81. package/dist/src/rules/index.js.map +1 -1
  82. package/dist/src/rules/logging/LoggerPayloadRule.d.ts +15 -0
  83. package/dist/src/rules/logging/LoggerPayloadRule.d.ts.map +1 -1
  84. package/dist/src/rules/logging/LoggerPayloadRule.js +48 -4
  85. package/dist/src/rules/logging/LoggerPayloadRule.js.map +1 -1
  86. package/dist/src/rules/operations/FlowRefTargetExistsRule.d.ts +23 -0
  87. package/dist/src/rules/operations/FlowRefTargetExistsRule.d.ts.map +1 -0
  88. package/dist/src/rules/operations/FlowRefTargetExistsRule.js +58 -0
  89. package/dist/src/rules/operations/FlowRefTargetExistsRule.js.map +1 -0
  90. package/dist/src/rules/operations/UnusedFlowRule.d.ts +26 -1
  91. package/dist/src/rules/operations/UnusedFlowRule.d.ts.map +1 -1
  92. package/dist/src/rules/operations/UnusedFlowRule.js +96 -16
  93. package/dist/src/rules/operations/UnusedFlowRule.js.map +1 -1
  94. package/dist/src/rules/operations/UnusedVariableRule.d.ts +31 -0
  95. package/dist/src/rules/operations/UnusedVariableRule.d.ts.map +1 -0
  96. package/dist/src/rules/operations/UnusedVariableRule.js +103 -0
  97. package/dist/src/rules/operations/UnusedVariableRule.js.map +1 -0
  98. package/dist/src/rules/performance/ConnectionPoolingRule.d.ts +5 -0
  99. package/dist/src/rules/performance/ConnectionPoolingRule.d.ts.map +1 -1
  100. package/dist/src/rules/performance/ConnectionPoolingRule.js +18 -5
  101. package/dist/src/rules/performance/ConnectionPoolingRule.js.map +1 -1
  102. package/dist/src/rules/performance/ListenerReconnectForeverRule.d.ts +28 -0
  103. package/dist/src/rules/performance/ListenerReconnectForeverRule.d.ts.map +1 -0
  104. package/dist/src/rules/performance/ListenerReconnectForeverRule.js +56 -0
  105. package/dist/src/rules/performance/ListenerReconnectForeverRule.js.map +1 -0
  106. package/dist/src/rules/performance/ReconnectionStrategyRule.d.ts +10 -0
  107. package/dist/src/rules/performance/ReconnectionStrategyRule.d.ts.map +1 -1
  108. package/dist/src/rules/performance/ReconnectionStrategyRule.js +47 -14
  109. package/dist/src/rules/performance/ReconnectionStrategyRule.js.map +1 -1
  110. package/dist/src/rules/security/ConnectorCredentialsSecuredRule.d.ts +36 -0
  111. package/dist/src/rules/security/ConnectorCredentialsSecuredRule.d.ts.map +1 -0
  112. package/dist/src/rules/security/ConnectorCredentialsSecuredRule.js +124 -0
  113. package/dist/src/rules/security/ConnectorCredentialsSecuredRule.js.map +1 -0
  114. package/dist/src/rules/security/HardcodedCredentialsRule.d.ts +4 -0
  115. package/dist/src/rules/security/HardcodedCredentialsRule.d.ts.map +1 -1
  116. package/dist/src/rules/security/HardcodedCredentialsRule.js +15 -0
  117. package/dist/src/rules/security/HardcodedCredentialsRule.js.map +1 -1
  118. package/dist/src/rules/security/SecurePropertiesEncryptionRule.d.ts +25 -0
  119. package/dist/src/rules/security/SecurePropertiesEncryptionRule.d.ts.map +1 -0
  120. package/dist/src/rules/security/SecurePropertiesEncryptionRule.js +59 -0
  121. package/dist/src/rules/security/SecurePropertiesEncryptionRule.js.map +1 -0
  122. package/dist/src/rules/security/SecurePropertiesKeyRule.d.ts +23 -0
  123. package/dist/src/rules/security/SecurePropertiesKeyRule.d.ts.map +1 -0
  124. package/dist/src/rules/security/SecurePropertiesKeyRule.js +45 -0
  125. package/dist/src/rules/security/SecurePropertiesKeyRule.js.map +1 -0
  126. package/dist/src/rules/security/TlsKeystorePasswordRule.d.ts +25 -0
  127. package/dist/src/rules/security/TlsKeystorePasswordRule.d.ts.map +1 -0
  128. package/dist/src/rules/security/TlsKeystorePasswordRule.js +63 -0
  129. package/dist/src/rules/security/TlsKeystorePasswordRule.js.map +1 -0
  130. package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.d.ts +26 -0
  131. package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.d.ts.map +1 -0
  132. package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.js +61 -0
  133. package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.js.map +1 -0
  134. package/dist/src/rules/standards/ConfigPropertiesOrderingRule.d.ts +34 -0
  135. package/dist/src/rules/standards/ConfigPropertiesOrderingRule.d.ts.map +1 -0
  136. package/dist/src/rules/standards/ConfigPropertiesOrderingRule.js +76 -0
  137. package/dist/src/rules/standards/ConfigPropertiesOrderingRule.js.map +1 -0
  138. package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.d.ts +25 -0
  139. package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.d.ts.map +1 -0
  140. package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.js +111 -0
  141. package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.js.map +1 -0
  142. package/dist/src/rules/structure/StructureRules.d.ts +8 -1
  143. package/dist/src/rules/structure/StructureRules.d.ts.map +1 -1
  144. package/dist/src/rules/structure/StructureRules.js +11 -7
  145. package/dist/src/rules/structure/StructureRules.js.map +1 -1
  146. package/dist/src/rules/yaml/YamlRules.d.ts +6 -2
  147. package/dist/src/rules/yaml/YamlRules.d.ts.map +1 -1
  148. package/dist/src/rules/yaml/YamlRules.js +15 -11
  149. package/dist/src/rules/yaml/YamlRules.js.map +1 -1
  150. package/dist/src/types/Rule.d.ts +35 -0
  151. package/dist/src/types/Rule.d.ts.map +1 -1
  152. package/docs/best-practices/rules-catalog.md +444 -42
  153. package/docs/linter/architecture.md +119 -64
  154. package/package.json +1 -1
package/README.md CHANGED
@@ -1,4 +1,13 @@
1
- # Mule-Lint
1
+ <p align="center">
2
+ <img src="assets/logo.svg" alt="Mule-Lint" width="600" />
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@sfdxy/mule-lint"><img src="https://img.shields.io/npm/v/@sfdxy/mule-lint?style=flat-square&color=34d399" alt="npm version" /></a>
7
+ <a href="https://github.com/Avinava/mule-lint/actions"><img src="https://img.shields.io/github/actions/workflow/status/Avinava/mule-lint/ci.yml?style=flat-square&color=38bdf8" alt="CI" /></a>
8
+ <a href="https://github.com/Avinava/mule-lint/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/@sfdxy/mule-lint?style=flat-square&color=818cf8" alt="License" /></a>
9
+ <a href="https://www.npmjs.com/package/@sfdxy/mule-lint"><img src="https://img.shields.io/npm/dm/@sfdxy/mule-lint?style=flat-square&color=fbbf24" alt="Downloads" /></a>
10
+ </p>
2
11
 
3
12
  <p align="center">
4
13
  <strong>Enterprise-grade static analysis tool for MuleSoft applications</strong>
@@ -19,12 +28,14 @@
19
28
 
20
29
  **Mule-Lint** is a TypeScript-based linting tool designed to enforce best practices and standards for MuleSoft applications. It provides:
21
30
 
22
- - **56 Built-in Rules** covering error handling, security, naming, logging, performance, and more
23
- - **Multiple Output Formats** - Table, JSON, SARIF, HTML, CSV <!-- id: 4 -->
24
- - **CI/CD Ready** - Exit codes and machine-readable output
25
- - **156+ Unit Tests** - Comprehensive test coverage for reliability
26
- - **TypeScript** - Fully typed for VS Code extension integration
27
- - **Extensible** - Add custom rules for your organization
31
+ - **82 Built-in Rules** covering error handling, security, naming, logging, performance, API-led, connectors, and more
32
+ - **Multiple Output Formats** - Table, JSON, SARIF, HTML, CSV <!-- id: 4 -->
33
+ - **CI/CD Ready** - Exit codes and machine-readable output
34
+ - **442 Unit Tests** - Comprehensive test coverage for reliability
35
+ - **TypeScript** - Fully typed for VS Code extension integration
36
+ - **Extensible** - Add custom rules for your organization
37
+ - **Document Cache** - Parsed XML documents cached to eliminate redundant parsing
38
+ - **Project Layer Detection** - Automatic SAPI/PAPI/EAPI/library/batch classification
28
39
 
29
40
  ### Architecture
30
41
 
@@ -35,15 +46,18 @@ flowchart TB
35
46
  end
36
47
 
37
48
  subgraph Engine["LintEngine"]
38
- B[FileScanner] --> C[XmlParser]
49
+ B[FileScanner] --> C[XmlParser + Document Cache]
39
50
  C --> D[Rule Executor]
51
+ B --> PreScan[Pre-Scan: allFlowRefs, allFlowNames, projectLayer]
52
+ PreScan --> D
40
53
  end
41
54
 
42
55
  subgraph Rules["Rules - Strategy Pattern"]
43
- D --> E[MULE-001]
44
- D --> F[MULE-002]
45
- D --> G[...]
46
- D --> H[MULE-010]
56
+ D --> E[Per-File Rules]
57
+ D --> F[Project Rules]
58
+ E --> G[MULE-001..804]
59
+ E --> H[SEC, ERR, API, DW, ...]
60
+ F --> I2[YAML, HYG, CFG, ...]
47
61
  end
48
62
 
49
63
  subgraph Output["Formatters"]
@@ -72,11 +86,12 @@ flowchart TB
72
86
 
73
87
  ```mermaid
74
88
  flowchart LR
75
- A["XML Files"] --> B["Parse DOM"]
76
- B --> C["Execute Rules"]
77
- C --> D["Collect Issues"]
78
- D --> E["Format Output"]
79
- E --> F["Table / JSON / SARIF / HTML / CSV"]
89
+ A["XML Files"] --> B["Pre-Scan (flow-refs, flow-names, project layer)"]
90
+ B --> C["Parse DOM (cached)"]
91
+ C --> D["Execute Rules"]
92
+ D --> E["Collect Issues"]
93
+ E --> F["Format Output"]
94
+ F --> G["Table / JSON / SARIF / HTML / CSV"]
80
95
  ```
81
96
 
82
97
  ---
@@ -273,55 +288,55 @@ npx @sfdxy/mule-lint src/main/mule -f sarif -o results.sarif
273
288
 
274
289
  ### Core Rules (MVP)
275
290
 
276
- | ID | Name | Severity | Category | Description |
277
- | -------- | --------------------- | -------- | -------------- | ------------------------------------------------- |
278
- | MULE-001 | Global Error Handler | Error | Error Handling | Project should have global error handler |
279
- | MULE-002 | Flow Naming | Warning | Naming | Flows end with `-flow`, sub-flows with `-subflow` |
280
- | MULE-003 | Missing Error Handler | Error | Error Handling | Flows should have error handlers |
281
- | MULE-004 | Hardcoded URLs | Error | Security | Use property placeholders for URLs |
282
- | MULE-005 | HTTP Status Check | Warning | Error Handling | Error handlers should set httpStatus |
283
- | MULE-006 | Logger Category | Warning | Logging | Loggers should have category attribute |
284
- | MULE-007 | Correlation ID | Warning | Error Handling | Error handlers should reference correlationId |
285
- | MULE-008 | Choice Anti-Pattern | Warning | Standards | Avoid raise-error in otherwise |
286
- | MULE-009 | Generic Error Type | Warning | Error Handling | Avoid catching type="ANY" |
287
- | MULE-010 | DWL Standards | Info | Standards | Standard DataWeave files should exist |
291
+ | ID | Name | Severity | Category | Description |
292
+ | -------- | --------------------- | -------- | -------------- | -------------------------------------------------------------------- |
293
+ | MULE-001 | Global Error Handler | Warning | Error Handling | Any flow file should have a global error handler |
294
+ | MULE-002 | Flow Naming | Warning | Naming | Flows end with `-flow`, sub-flows with `-subflow` |
295
+ | MULE-003 | Missing Error Handler | Error | Error Handling | Flows should have error handlers |
296
+ | MULE-004 | Hardcoded URLs | Error | Security | Use property placeholders for URLs |
297
+ | MULE-005 | HTTP Status Check | Warning | Error Handling | Error handlers should set httpStatus (skipped for non-HTTP projects) |
298
+ | MULE-006 | Logger Category | Warning | Logging | Loggers should have category attribute |
299
+ | MULE-007 | Correlation ID | Warning | Error Handling | Error handlers should reference correlationId (checks DWL files too) |
300
+ | MULE-008 | Choice Anti-Pattern | Warning | Standards | Avoid raise-error in otherwise |
301
+ | MULE-009 | Generic Error Type | Warning | Error Handling | Avoid catching type="ANY" |
302
+ | MULE-010 | DWL Standards | Info | Standards | Standard DataWeave files should exist |
288
303
 
289
304
  ### Extended Rules
290
305
 
291
- | ID | Name | Severity | Category | Description |
292
- | -------- | --------------------- | -------- | ------------- | --------------------------------- |
293
- | MULE-101 | Flow Casing | Warning | Naming | kebab-case for flows |
294
- | MULE-102 | Variable Naming | Warning | Naming | camelCase for variables |
295
- | MULE-201 | Hardcoded Credentials | Error | Security | Use `${secure::}` |
296
- | MULE-202 | Insecure TLS | Error | Security | No insecure="true" |
297
- | MULE-301 | Logger Payload | Warning | Logging | Don't log entire payload |
298
- | MULE-303 | Logger in Retry | Warning | Logging | Avoid loggers in until-successful |
299
- | MULE-401 | HTTP User-Agent | Warning | HTTP | Include User-Agent |
300
- | MULE-402 | HTTP Content-Type | Warning | HTTP | POST/PUT needs Content-Type |
301
- | MULE-403 | HTTP Timeout | Warning | HTTP | Set responseTimeout |
302
- | MULE-501 | Scatter-Gather | Info | Performance | Limit parallel routes |
303
- | MULE-502 | Async Error | Warning | Performance | Async needs error handling |
304
- | MULE-503 | Large Choice | Warning | Performance | Max 7 when clauses |
305
- | MULE-601 | Flow Description | Info | Documentation | Add doc:description |
306
- | MULE-604 | Missing doc:name | Warning | Documentation | Key components need doc:name |
307
- | MULE-701 | Deprecated | Warning | Standards | Detect deprecated elements |
308
- | MULE-801 | Flow Complexity | Warning | Complexity | Cyclomatic complexity threshold |
309
- | MULE-802 | Project Structure | Warning | Structure | Validate folder structure |
310
- | MULE-803 | Global Config | Warning | Structure | global.xml should exist |
311
- | MULE-804 | Monolithic XML | Warning | Structure | Split large XML files |
306
+ | ID | Name | Severity | Category | Description |
307
+ | -------- | --------------------- | -------- | ------------- | --------------------------------------------------------- |
308
+ | MULE-101 | Flow Casing | Warning | Naming | kebab-case for flows |
309
+ | MULE-102 | Variable Naming | Warning | Naming | camelCase for variables |
310
+ | MULE-201 | Hardcoded Credentials | Error | Security | Use `${secure::}` |
311
+ | MULE-202 | Insecure TLS | Error | Security | No insecure="true" |
312
+ | MULE-301 | Logger Payload | Warning | Logging | Don't log entire payload |
313
+ | MULE-303 | Logger in Retry | Warning | Logging | Avoid loggers in until-successful |
314
+ | MULE-401 | HTTP User-Agent | Warning | HTTP | Include User-Agent |
315
+ | MULE-402 | HTTP Content-Type | Warning | HTTP | POST/PUT needs Content-Type (static, CDATA, or inline DW) |
316
+ | MULE-403 | HTTP Timeout | Warning | HTTP | Set responseTimeout |
317
+ | MULE-501 | Scatter-Gather | Info | Performance | Limit parallel routes |
318
+ | MULE-502 | Async Error | Warning | Performance | Async needs error handling |
319
+ | MULE-503 | Large Choice | Warning | Performance | Max 7 when clauses |
320
+ | MULE-601 | Flow Description | Info | Documentation | Add doc:description |
321
+ | MULE-604 | Missing doc:name | Warning | Documentation | Key components need doc:name |
322
+ | MULE-701 | Deprecated | Warning | Standards | Detect deprecated elements |
323
+ | MULE-801 | Flow Complexity | Warning | Complexity | Cyclomatic complexity threshold |
324
+ | MULE-802 | Project Structure | Warning | Structure | Validate folder structure |
325
+ | MULE-803 | Global Config | Warning | Structure | global.xml should exist |
326
+ | MULE-804 | Monolithic XML | Warning | Structure | Split large XML files |
312
327
 
313
328
  ### DataWeave & API-Led Rules
314
329
 
315
- | ID | Name | Severity | Category | Description |
316
- | ------- | ----------------- | -------- | --------- | --------------------------------- |
317
- | DW-001 | External DWL | Warning | DataWeave | Externalize complex transforms |
318
- | DW-002 | DWL Naming | Info | DataWeave | kebab-case for .dwl files |
319
- | DW-003 | DWL Modules | Info | DataWeave | Use common modules |
320
- | DW-004 | Java 17 DW Errors | Error | DataWeave | Java 17 compatible error handling |
321
- | API-001 | Experience Layer | Info | API-Led | Experience API patterns |
322
- | API-002 | Process Layer | Info | API-Led | Process layer orchestration |
323
- | API-003 | System Layer | Info | API-Led | System layer connections |
324
- | API-004 | Single SAPI | Warning | API-Led | Single system per SAPI |
330
+ | ID | Name | Severity | Category | Description |
331
+ | ------- | ----------------- | -------- | --------- | ---------------------------------------------------- |
332
+ | DW-001 | External DWL | Warning | DataWeave | Externalize complex transforms |
333
+ | DW-002 | DWL Naming | Info | DataWeave | kebab-case for .dwl files (configurable exemptPaths) |
334
+ | DW-003 | DWL Modules | Info | DataWeave | Use common modules |
335
+ | DW-004 | Java 17 DW Errors | Error | DataWeave | Java 17 compatible error handling |
336
+ | API-001 | Experience Layer | Info | API-Led | Experience API patterns |
337
+ | API-002 | Process Layer | Info | API-Led | Process layer orchestration |
338
+ | API-003 | System Layer | Info | API-Led | System layer connections |
339
+ | API-004 | Single SAPI | Warning | API-Led | Single system per SAPI |
325
340
 
326
341
  ### Experimental Rules
327
342
 
@@ -346,20 +361,60 @@ npx @sfdxy/mule-lint src/main/mule -f sarif -o results.sarif
346
361
  | ERR-001 | Try Scope | Info | Error Handling | Complex operations should use Try scope |
347
362
  | PERF-002 | Connection Pooling | Warning | Performance | DB/HTTP should configure connection pools |
348
363
 
364
+ ### Security & Integrity Rules (v1.21)
365
+
366
+ | ID | Name | Severity | Category | Description |
367
+ | ------- | ---------------------------- | -------- | --------- | ------------------------------------------------------- |
368
+ | SEC-007 | Connector Credentials | Error | Security | Connector configs must use secure property placeholders |
369
+ | SEC-008 | Secure Properties Key | Error | Security | Secure properties file must use externalized key |
370
+ | SEC-009 | TLS Keystore Password | Error | Security | TLS keystore passwords must use secure properties |
371
+ | SEC-010 | Secure Properties Encryption | Warning | Security | Secure properties must use strong encryption algorithm |
372
+ | HYG-004 | Flow-Ref Target Exists | Error | Standards | Flow-ref must point to an existing flow/sub-flow |
373
+
374
+ ### Error Handling & Resilience Rules (v1.21)
375
+
376
+ | ID | Name | Severity | Category | Description |
377
+ | ------- | -------------------------- | -------- | -------------- | ----------------------------------------------------------- |
378
+ | ERR-002 | Error Type Coverage | Warning | Error Handling | APIKit flows should handle common HTTP error types |
379
+ | ERR-003 | Error Response Structure | Warning | Error Handling | Error handlers should set both httpStatus and response body |
380
+ | ERR-004 | Catch-All Must Be Last | Error | Error Handling | type="ANY" on-error must be the last handler block |
381
+ | RES-002 | Listener Reconnect Forever | Warning | Performance | Listener connectors should use reconnect-forever strategy |
382
+ | CFG-001 | Config Properties Ordering | Info | Standards | Config properties should follow a consistent ordering |
383
+
384
+ ### API-Led & Connector Rules (v1.21)
385
+
386
+ | ID | Name | Severity | Category | Description |
387
+ | -------- | ----------------------- | -------- | --------- | ------------------------------------------------------- |
388
+ | API-006 | APIKit Main Flow | Warning | API-Led | APIKit main flow should follow standard structure |
389
+ | API-007 | APIKit Status Code Var | Warning | API-Led | APIKit routes should set httpStatus variable |
390
+ | API-008 | APIKit Console Prod | Warning | API-Led | APIKit console should be disabled in production |
391
+ | SF-001 | Replay Channel Config | Warning | Connector | Salesforce replay channel should have proper config |
392
+ | HTTP-004 | Connection Idle Timeout | Warning | HTTP | HTTP request configs should set connection idle timeout |
393
+
394
+ ### Code Hygiene Rules (v1.21)
395
+
396
+ | ID | Name | Severity | Category | Description |
397
+ | ------- | ---------------------------- | -------- | --------- | ---------------------------------------------------------- |
398
+ | DW-005 | Duplicate Transform Logic | Warning | DataWeave | Detect duplicated DataWeave transform expressions |
399
+ | HYG-005 | Unused Variable | Warning | Standards | Detect set-variable values never referenced in the project |
400
+ | CFG-002 | Missing Env Properties | Warning | Standards | Properties referenced in XML should exist in YAML configs |
401
+ | STD-001 | APIKit Route Var Consistency | Info | Standards | APIKit route variable names should be consistent |
402
+ | SF-002 | Event Listener Null Guard | Warning | Connector | Event listeners should guard against null payloads |
403
+
349
404
  ### Operations & Resilience Rules
350
405
 
351
- | ID | Name | Severity | Category | Description |
352
- | ------- | ---------------------- | -------- | ------------- | ----------------------------------------------- |
353
- | RES-001 | Reconnection Strategy | Warning | Performance | Connectors should have reconnection strategies |
354
- | OPS-001 | Auto-Discovery | Info | Standards | APIs should have auto-discovery for API Manager |
355
- | OPS-002 | HTTP Port Placeholder | Warning | Standards | HTTP ports should use property placeholders |
356
- | OPS-003 | Externalized Cron | Warning | Standards | Cron expressions should use placeholders |
357
- | SEC-006 | Encryption Key in Logs | Error | Security | Detect sensitive data in log messages |
358
- | HYG-001 | Excessive Loggers | Warning | Logging | Flows should not have too many loggers |
359
- | HYG-002 | Commented Code | Info | Standards | Detect commented-out code blocks |
360
- | HYG-003 | Unused Flow | Warning | Standards | Detect flows/sub-flows never referenced |
361
- | API-005 | APIKit Validation | Info | Standards | APIs should use APIKit for interfaces |
362
- | DOC-001 | Display Name | Info | Documentation | Key components should have meaningful names |
406
+ | ID | Name | Severity | Category | Description |
407
+ | ------- | ---------------------- | -------- | ------------- | ---------------------------------------------------- |
408
+ | RES-001 | Reconnection Strategy | Warning | Performance | Connectors should have reconnection strategies |
409
+ | OPS-001 | Auto-Discovery | Info | Standards | APIs should have auto-discovery for API Manager |
410
+ | OPS-002 | HTTP Port Placeholder | Warning | Standards | HTTP ports should use property placeholders |
411
+ | OPS-003 | Externalized Cron | Warning | Standards | Cron expressions should use placeholders |
412
+ | SEC-006 | Encryption Key in Logs | Error | Security | Detect sensitive data in log messages |
413
+ | HYG-001 | Excessive Loggers | Warning | Logging | Flows should not have too many loggers |
414
+ | HYG-002 | Commented Code | Info | Standards | Detect commented-out code blocks |
415
+ | HYG-003 | Unused Flow | Warning | Standards | Detect flows/sub-flows never referenced (cross-file) |
416
+ | API-005 | APIKit Validation | Info | Standards | APIs should use APIKit for interfaces |
417
+ | DOC-001 | Display Name | Info | Documentation | Key components should have meaningful names |
363
418
 
364
419
  ### Project Governance Rules
365
420
 
@@ -368,7 +423,7 @@ npx @sfdxy/mule-lint src/main/mule -f sarif -o results.sarif
368
423
  | PROJ-001 | POM Validation | Error | Structure | Validates pom.xml existence and plugins |
369
424
  | PROJ-002 | Git Hygiene | Warning | Structure | Validates .gitignore existence and entries |
370
425
 
371
- **Total: 56 rules** across 14 categories.
426
+ **Total: 82 rules** across 16 categories.
372
427
 
373
428
  See [Rules Catalog](docs/best-practices/rules-catalog.md) for detailed documentation.
374
429
 
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfdxy/mule-lint",
3
- "version": "1.19.0",
3
+ "version": "1.21.0",
4
4
  "description": "Static analysis tool for MuleSoft applications - supports humans, AI agents, and CI/CD pipelines",
5
5
  "author": "Avinava",
6
6
  "license": "MIT",
@@ -1 +1 @@
1
- {"version":3,"file":"XPathHelper.d.ts","sourceRoot":"","sources":["../../../src/core/XPathHelper.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA6DlD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,IAAI;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAE3C,OAAO;IAIP;;OAEG;WACW,WAAW,IAAI,WAAW;IAOxC;;OAEG;WACW,KAAK,IAAI,IAAI;IAI3B;;;;;OAKG;IACI,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,EAAE;IAaxE;;;;;OAKG;IACI,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;IAK5E;;;;;OAKG;IACI,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI;IAShF;;;;;OAKG;IACI,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,OAAO;IAIpE;;;;;OAKG;IACI,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM;CAGnE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQxE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEhD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAE9D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGlE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEjD"}
1
+ {"version":3,"file":"XPathHelper.d.ts","sourceRoot":"","sources":["../../../src/core/XPathHelper.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAyElD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,IAAI;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAE3C,OAAO;IAIP;;OAEG;WACW,WAAW,IAAI,WAAW;IAOxC;;OAEG;WACW,KAAK,IAAI,IAAI;IAI3B;;;;;OAKG;IACI,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,EAAE;IAaxE;;;;;OAKG;IACI,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;IAK5E;;;;;OAKG;IACI,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI;IAShF;;;;;OAKG;IACI,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,OAAO;IAIpE;;;;;OAKG;IACI,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM;CAGnE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQxE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEhD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAE9D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGlE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEjD"}
@@ -91,6 +91,14 @@ exports.MULE_NAMESPACES = {
91
91
  sockets: 'http://www.mulesoft.org/schema/mule/sockets',
92
92
  // Web Service Consumer
93
93
  wsc: 'http://www.mulesoft.org/schema/mule/wsc',
94
+ // NetSuite
95
+ netsuite: 'http://www.mulesoft.org/schema/mule/netsuite',
96
+ // SAP
97
+ sap: 'http://www.mulesoft.org/schema/mule/sap',
98
+ // Anypoint MQ
99
+ 'anypoint-mq': 'http://www.mulesoft.org/schema/mule/anypoint-mq',
100
+ // OAuth
101
+ oauth: 'http://www.mulesoft.org/schema/mule/oauth',
94
102
  };
95
103
  /**
96
104
  * Helper class for namespace-aware XPath queries on Mule XML documents
@@ -1 +1 @@
1
- {"version":3,"file":"XPathHelper.js","sourceRoot":"","sources":["../../../src/core/XPathHelper.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"XPathHelper.js","sourceRoot":"","sources":["../../../src/core/XPathHelper.ts"],"names":[],"mappings":"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"}
@@ -19,6 +19,12 @@ export declare class LintEngine {
19
19
  private rules;
20
20
  private config;
21
21
  private verbose;
22
+ /**
23
+ * Cache of parsed XML documents keyed by absolute file path.
24
+ * Populated during preScanFiles() and reused in processFile()
25
+ * to avoid parsing every XML file twice.
26
+ */
27
+ private documentCache;
22
28
  constructor(options: EngineOptions);
23
29
  /**
24
30
  * Scan a directory or file and return lint report
@@ -64,5 +70,31 @@ export declare class LintEngine {
64
70
  * Collect metrics from a parsed XML document
65
71
  */
66
72
  private collectFileMetrics;
73
+ /**
74
+ * Pre-scan all XML files to build cross-file context:
75
+ * - allFlowRefs: union of all <flow-ref name="..."> targets across files
76
+ * - allFlowNames: union of all <flow>/<sub-flow> name attributes across files
77
+ * - projectContext: whether the project has HTTP listeners / APIkit routers
78
+ *
79
+ * Parsed documents are cached in this.documentCache so that processFile()
80
+ * can reuse them instead of re-parsing.
81
+ *
82
+ * This runs before the main per-file rule execution so that rules can use
83
+ * the aggregated information (e.g. HYG-003 cross-file unused flow detection,
84
+ * HYG-004 cross-file flow-ref target validation,
85
+ * MULE-005 HTTP-only project detection).
86
+ */
87
+ private preScanFiles;
88
+ /**
89
+ * Detect the API-led connectivity layer of the project.
90
+ *
91
+ * Heuristics (in priority order):
92
+ * 1. Directory/project name contains `-sapi`, `-papi`, `-eapi`
93
+ * 2. Flow names contain `sapi`, `papi`, `eapi` patterns
94
+ * 3. No HTTP listener + batch jobs → batch
95
+ * 4. No flows at all → library
96
+ * 5. Otherwise → unknown
97
+ */
98
+ private detectProjectLayer;
67
99
  }
68
100
  //# sourceMappingURL=LintEngine.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LintEngine.d.ts","sourceRoot":"","sources":["../../../src/engine/LintEngine.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,KAAK,EAA2C,MAAM,UAAU,CAAC;AAChF,OAAO,EAAE,UAAU,EAAkB,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAA2C,MAAM,iBAAiB,CAAC;AAQtF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+BAA+B;IAC/B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7B,sBAAsB;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAU;gBAEb,OAAO,EAAE,aAAa;IAMlC;;OAEG;IACU,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAoH1D;;OAEG;IACH,OAAO,CAAC,eAAe;IAgBvB;;OAEG;IACI,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,EAAE;IAmB9D;;OAEG;IACI,eAAe,IAAI,IAAI,EAAE;IAOhC;;OAEG;IACH,OAAO,CAAC,WAAW;IAoDnB;;OAEG;IACH,OAAO,CAAC,QAAQ;IA4ChB;;OAEG;IACH,OAAO,CAAC,aAAa;IAerB;;OAEG;IACH,OAAO,CAAC,YAAY;IAgCpB;;OAEG;IACH,OAAO,CAAC,eAAe;IA+BvB;;OAEG;IACH,OAAO,CAAC,GAAG;IAOX;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAG3B"}
1
+ {"version":3,"file":"LintEngine.d.ts","sourceRoot":"","sources":["../../../src/engine/LintEngine.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,IAAI,EACJ,KAAK,EAMN,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,UAAU,EAAkB,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAA2C,MAAM,iBAAiB,CAAC;AAQtF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+BAA+B;IAC/B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7B,sBAAsB;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAU;IAEzB;;;;OAIG;IACH,OAAO,CAAC,aAAa,CAAoC;gBAE7C,OAAO,EAAE,aAAa;IAMlC;;OAEG;IACU,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAoI1D;;OAEG;IACH,OAAO,CAAC,eAAe;IAgBvB;;OAEG;IACI,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,EAAE;IAmB9D;;OAEG;IACI,eAAe,IAAI,IAAI,EAAE;IAOhC;;OAEG;IACH,OAAO,CAAC,WAAW;IAqEnB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAuDhB;;OAEG;IACH,OAAO,CAAC,aAAa;IAerB;;OAEG;IACH,OAAO,CAAC,YAAY;IAgCpB;;OAEG;IACH,OAAO,CAAC,eAAe;IA+BvB;;OAEG;IACH,OAAO,CAAC,GAAG;IAOX;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAI1B;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,YAAY;IAgFpB;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB;CA2C3B"}
@@ -54,6 +54,12 @@ class LintEngine {
54
54
  rules;
55
55
  config;
56
56
  verbose;
57
+ /**
58
+ * Cache of parsed XML documents keyed by absolute file path.
59
+ * Populated during preScanFiles() and reused in processFile()
60
+ * to avoid parsing every XML file twice.
61
+ */
62
+ documentCache = new Map();
57
63
  constructor(options) {
58
64
  this.rules = options.rules;
59
65
  this.config = { ...Config_1.DEFAULT_CONFIG, ...options.config };
@@ -88,6 +94,10 @@ class LintEngine {
88
94
  exclude: this.config.exclude,
89
95
  });
90
96
  this.log(`Found ${files.length} files to scan`);
97
+ // Pre-scan: collect cross-file context before rule execution
98
+ const { allFlowRefs, allFlowNames, projectContext } = isStandalone
99
+ ? { allFlowRefs: undefined, allFlowNames: undefined, projectContext: undefined }
100
+ : this.preScanFiles(files);
91
101
  // Process each file and collect metrics
92
102
  const fileResults = [];
93
103
  const metricsAggregator = {
@@ -108,7 +118,7 @@ class LintEngine {
108
118
  flowComplexityData: [],
109
119
  };
110
120
  for (const file of files) {
111
- const result = this.processFile(file, projectRoot, isStandalone, metricsAggregator);
121
+ const result = this.processFile(file, projectRoot, isStandalone, metricsAggregator, allFlowRefs, allFlowNames, projectContext);
112
122
  fileResults.push(result);
113
123
  }
114
124
  // Run Project Rules (only once per scan, if not standalone)
@@ -148,6 +158,8 @@ class LintEngine {
148
158
  const summary = this.buildSummary(fileResults);
149
159
  this.log(`Scan complete in ${durationMs}ms`);
150
160
  this.log(`Found ${summary.bySeverity.error} errors, ${summary.bySeverity.warning} warnings`);
161
+ // Release cached documents to free memory
162
+ this.documentCache.clear();
151
163
  // Build initial report with base metrics
152
164
  const baseReport = {
153
165
  projectRoot,
@@ -210,24 +222,34 @@ class LintEngine {
210
222
  /**
211
223
  * Process a single file
212
224
  */
213
- processFile(file, projectRoot, isStandalone = false, metricsAggregator) {
225
+ processFile(file, projectRoot, isStandalone = false, metricsAggregator, allFlowRefs, allFlowNames, projectContext) {
214
226
  this.log(` Processing: ${file.relativePath}`);
215
227
  try {
216
- const content = (0, FileScanner_1.readFileContent)(file.absolutePath);
217
- const parseResult = (0, XmlParser_1.parseXml)(content, file.relativePath);
218
- if (!parseResult.success || !parseResult.document) {
219
- return {
220
- filePath: file.absolutePath,
221
- relativePath: file.relativePath,
222
- issues: [],
223
- parsed: false,
224
- parseError: parseResult.error,
225
- };
228
+ // Try to use the cached document from preScanFiles() first
229
+ const cachedDoc = this.documentCache.get(file.absolutePath);
230
+ let doc;
231
+ if (cachedDoc) {
232
+ doc = cachedDoc;
233
+ }
234
+ else {
235
+ // Cache miss (standalone mode or non-XML processed first time)
236
+ const content = (0, FileScanner_1.readFileContent)(file.absolutePath);
237
+ const parseResult = (0, XmlParser_1.parseXml)(content, file.relativePath);
238
+ if (!parseResult.success || !parseResult.document) {
239
+ return {
240
+ filePath: file.absolutePath,
241
+ relativePath: file.relativePath,
242
+ issues: [],
243
+ parsed: false,
244
+ parseError: parseResult.error,
245
+ };
246
+ }
247
+ doc = parseResult.document;
226
248
  }
227
- const issues = this.runRules(parseResult.document, file.absolutePath, projectRoot, isStandalone);
249
+ const issues = this.runRules(doc, file.absolutePath, projectRoot, isStandalone, allFlowRefs, allFlowNames, projectContext);
228
250
  // Collect metrics from parsed document
229
251
  if (metricsAggregator) {
230
- this.collectFileMetrics(parseResult.document, file.relativePath, metricsAggregator);
252
+ this.collectFileMetrics(doc, file.relativePath, metricsAggregator);
231
253
  }
232
254
  return {
233
255
  filePath: file.absolutePath,
@@ -250,7 +272,7 @@ class LintEngine {
250
272
  /**
251
273
  * Run all enabled rules against a document
252
274
  */
253
- runRules(doc, filePath, projectRoot, isStandalone = false) {
275
+ runRules(doc, filePath, projectRoot, isStandalone = false, allFlowRefs, allFlowNames, projectContext) {
254
276
  const issues = [];
255
277
  const enabledRules = this.getEnabledRules();
256
278
  for (const rule of enabledRules) {
@@ -258,12 +280,19 @@ class LintEngine {
258
280
  if (isStandalone && rule.category === 'structure') {
259
281
  continue;
260
282
  }
283
+ // Skip ProjectRule instances — they are handled separately in runProjectRules()
284
+ if (rule instanceof ProjectRule_1.ProjectRule) {
285
+ continue;
286
+ }
261
287
  try {
262
288
  const context = {
263
289
  filePath,
264
290
  relativePath: path.relative(projectRoot, filePath),
265
291
  projectRoot,
266
292
  config: this.getRuleConfig(rule.id),
293
+ allFlowRefs,
294
+ allFlowNames,
295
+ projectContext,
267
296
  };
268
297
  const ruleIssues = rule.validate(doc, context);
269
298
  // Apply severity override from config
@@ -371,6 +400,128 @@ class LintEngine {
371
400
  collectFileMetrics(doc, relativePath, metrics) {
372
401
  (0, MetricsCollector_1.collectFileMetrics)(doc, relativePath, metrics);
373
402
  }
403
+ /**
404
+ * Pre-scan all XML files to build cross-file context:
405
+ * - allFlowRefs: union of all <flow-ref name="..."> targets across files
406
+ * - allFlowNames: union of all <flow>/<sub-flow> name attributes across files
407
+ * - projectContext: whether the project has HTTP listeners / APIkit routers
408
+ *
409
+ * Parsed documents are cached in this.documentCache so that processFile()
410
+ * can reuse them instead of re-parsing.
411
+ *
412
+ * This runs before the main per-file rule execution so that rules can use
413
+ * the aggregated information (e.g. HYG-003 cross-file unused flow detection,
414
+ * HYG-004 cross-file flow-ref target validation,
415
+ * MULE-005 HTTP-only project detection).
416
+ */
417
+ preScanFiles(files) {
418
+ const allFlowRefs = new Set();
419
+ const allFlowNames = new Set();
420
+ const projectContext = {
421
+ hasHttpListener: false,
422
+ hasApikitRouter: false,
423
+ };
424
+ // Clear the cache at the start of each scan
425
+ this.documentCache.clear();
426
+ for (const file of files) {
427
+ // Only process XML files
428
+ if (!file.absolutePath.endsWith('.xml')) {
429
+ continue;
430
+ }
431
+ try {
432
+ const content = (0, FileScanner_1.readFileContent)(file.absolutePath);
433
+ const parseResult = (0, XmlParser_1.parseXml)(content, file.relativePath);
434
+ if (!parseResult.success || !parseResult.document) {
435
+ continue;
436
+ }
437
+ const doc = parseResult.document;
438
+ // Cache the parsed document for reuse in processFile()
439
+ this.documentCache.set(file.absolutePath, doc);
440
+ // Collect flow-ref targets
441
+ const allElements = doc.getElementsByTagName('*');
442
+ for (let i = 0; i < allElements.length; i++) {
443
+ const el = allElements[i];
444
+ const localName = el.localName ?? el.nodeName.split(':').pop() ?? '';
445
+ if (localName === 'flow-ref') {
446
+ const name = el.getAttribute('name');
447
+ if (name) {
448
+ allFlowRefs.add(name);
449
+ }
450
+ }
451
+ // Collect flow/sub-flow definitions
452
+ if (localName === 'flow' || localName === 'sub-flow') {
453
+ const name = el.getAttribute('name');
454
+ if (name) {
455
+ allFlowNames.add(name);
456
+ }
457
+ }
458
+ // Detect HTTP listener
459
+ if (localName === 'listener') {
460
+ projectContext.hasHttpListener = true;
461
+ }
462
+ // Detect APIkit router or console
463
+ if (localName === 'router' || localName === 'console') {
464
+ // Check namespace to be sure it's apikit
465
+ const nodeName = el.nodeName;
466
+ if (nodeName.includes('apikit:')) {
467
+ projectContext.hasApikitRouter = true;
468
+ }
469
+ }
470
+ }
471
+ }
472
+ catch {
473
+ // Ignore unreadable files during pre-scan
474
+ }
475
+ }
476
+ // Detect project layer from directory name, flow names, and content
477
+ projectContext.projectLayer = this.detectProjectLayer(files, allFlowNames, projectContext);
478
+ return { allFlowRefs, allFlowNames, projectContext };
479
+ }
480
+ /**
481
+ * Detect the API-led connectivity layer of the project.
482
+ *
483
+ * Heuristics (in priority order):
484
+ * 1. Directory/project name contains `-sapi`, `-papi`, `-eapi`
485
+ * 2. Flow names contain `sapi`, `papi`, `eapi` patterns
486
+ * 3. No HTTP listener + batch jobs → batch
487
+ * 4. No flows at all → library
488
+ * 5. Otherwise → unknown
489
+ */
490
+ detectProjectLayer(files, allFlowNames, projectContext) {
491
+ // Check project directory name
492
+ const projectDir = files.length > 0
493
+ ? path.basename(path.resolve(files[0].absolutePath, '..', '..', '..', '..'))
494
+ : '';
495
+ const dirLower = projectDir.toLowerCase();
496
+ if (dirLower.includes('-sapi') || dirLower.includes('_sapi') || dirLower.endsWith('sapi')) {
497
+ return 'sapi';
498
+ }
499
+ if (dirLower.includes('-papi') || dirLower.includes('_papi') || dirLower.endsWith('papi')) {
500
+ return 'papi';
501
+ }
502
+ if (dirLower.includes('-eapi') || dirLower.includes('_eapi') || dirLower.endsWith('eapi')) {
503
+ return 'eapi';
504
+ }
505
+ // Check flow names for layer hints
506
+ const flowNamesArray = [...allFlowNames].map((n) => n.toLowerCase());
507
+ const hasSapiFlow = flowNamesArray.some((n) => n.includes('sapi') || n.includes('system-api'));
508
+ const hasPapiFlow = flowNamesArray.some((n) => n.includes('papi') || n.includes('process-api'));
509
+ const hasEapiFlow = flowNamesArray.some((n) => n.includes('eapi') || n.includes('experience-api'));
510
+ if (hasSapiFlow && !hasPapiFlow && !hasEapiFlow)
511
+ return 'sapi';
512
+ if (hasPapiFlow && !hasSapiFlow && !hasEapiFlow)
513
+ return 'papi';
514
+ if (hasEapiFlow && !hasSapiFlow && !hasPapiFlow)
515
+ return 'eapi';
516
+ // No flows → library
517
+ if (allFlowNames.size === 0)
518
+ return 'library';
519
+ // Has batch jobs but no HTTP listener → batch
520
+ const hasBatch = flowNamesArray.some((n) => n.includes('batch'));
521
+ if (hasBatch && !projectContext.hasHttpListener)
522
+ return 'batch';
523
+ return 'unknown';
524
+ }
374
525
  }
375
526
  exports.LintEngine = LintEngine;
376
527
  //# sourceMappingURL=LintEngine.js.map