@sfdxy/mule-lint 1.20.0 → 1.22.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 (156) hide show
  1. package/README.md +63 -17
  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 +22 -0
  7. package/dist/src/engine/LintEngine.d.ts.map +1 -1
  8. package/dist/src/engine/LintEngine.js +105 -18
  9. package/dist/src/engine/LintEngine.js.map +1 -1
  10. package/dist/src/mcp/prompts/index.d.ts +1 -1
  11. package/dist/src/mcp/prompts/index.d.ts.map +1 -1
  12. package/dist/src/mcp/prompts/index.js +62 -1
  13. package/dist/src/mcp/prompts/index.js.map +1 -1
  14. package/dist/src/mcp/resources/index.js +114 -16
  15. package/dist/src/mcp/resources/index.js.map +1 -1
  16. package/dist/src/mcp/tools/getRuleDetails.d.ts.map +1 -1
  17. package/dist/src/mcp/tools/getRuleDetails.js +30 -1
  18. package/dist/src/mcp/tools/getRuleDetails.js.map +1 -1
  19. package/dist/src/rules/api-led/ApikitConsoleProductionRule.d.ts +22 -0
  20. package/dist/src/rules/api-led/ApikitConsoleProductionRule.d.ts.map +1 -0
  21. package/dist/src/rules/api-led/ApikitConsoleProductionRule.js +43 -0
  22. package/dist/src/rules/api-led/ApikitConsoleProductionRule.js.map +1 -0
  23. package/dist/src/rules/api-led/ApikitMainFlowStructureRule.d.ts +24 -0
  24. package/dist/src/rules/api-led/ApikitMainFlowStructureRule.d.ts.map +1 -0
  25. package/dist/src/rules/api-led/ApikitMainFlowStructureRule.js +53 -0
  26. package/dist/src/rules/api-led/ApikitMainFlowStructureRule.js.map +1 -0
  27. package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.d.ts +25 -0
  28. package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.d.ts.map +1 -0
  29. package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.js +59 -0
  30. package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.js.map +1 -0
  31. package/dist/src/rules/connector/EventListenerNullGuardRule.d.ts +24 -0
  32. package/dist/src/rules/connector/EventListenerNullGuardRule.d.ts.map +1 -0
  33. package/dist/src/rules/connector/EventListenerNullGuardRule.js +58 -0
  34. package/dist/src/rules/connector/EventListenerNullGuardRule.js.map +1 -0
  35. package/dist/src/rules/connector/ReplayChannelConfigRule.d.ts +23 -0
  36. package/dist/src/rules/connector/ReplayChannelConfigRule.d.ts.map +1 -0
  37. package/dist/src/rules/connector/ReplayChannelConfigRule.js +52 -0
  38. package/dist/src/rules/connector/ReplayChannelConfigRule.js.map +1 -0
  39. package/dist/src/rules/dataweave/DataWeaveRules.d.ts +11 -4
  40. package/dist/src/rules/dataweave/DataWeaveRules.d.ts.map +1 -1
  41. package/dist/src/rules/dataweave/DataWeaveRules.js +20 -20
  42. package/dist/src/rules/dataweave/DataWeaveRules.js.map +1 -1
  43. package/dist/src/rules/dataweave/DuplicateTransformLogicRule.d.ts +25 -0
  44. package/dist/src/rules/dataweave/DuplicateTransformLogicRule.d.ts.map +1 -0
  45. package/dist/src/rules/dataweave/DuplicateTransformLogicRule.js +63 -0
  46. package/dist/src/rules/dataweave/DuplicateTransformLogicRule.js.map +1 -0
  47. package/dist/src/rules/error-handling/CatchAllLastRule.d.ts +24 -0
  48. package/dist/src/rules/error-handling/CatchAllLastRule.d.ts.map +1 -0
  49. package/dist/src/rules/error-handling/CatchAllLastRule.js +65 -0
  50. package/dist/src/rules/error-handling/CatchAllLastRule.js.map +1 -0
  51. package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.d.ts +28 -0
  52. package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.d.ts.map +1 -0
  53. package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.js +70 -0
  54. package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.js.map +1 -0
  55. package/dist/src/rules/error-handling/ErrorResponseStructureRule.d.ts +23 -0
  56. package/dist/src/rules/error-handling/ErrorResponseStructureRule.d.ts.map +1 -0
  57. package/dist/src/rules/error-handling/ErrorResponseStructureRule.js +73 -0
  58. package/dist/src/rules/error-handling/ErrorResponseStructureRule.js.map +1 -0
  59. package/dist/src/rules/error-handling/GenericErrorRule.d.ts +15 -3
  60. package/dist/src/rules/error-handling/GenericErrorRule.d.ts.map +1 -1
  61. package/dist/src/rules/error-handling/GenericErrorRule.js +58 -18
  62. package/dist/src/rules/error-handling/GenericErrorRule.js.map +1 -1
  63. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts +14 -15
  64. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts.map +1 -1
  65. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js +59 -38
  66. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js.map +1 -1
  67. package/dist/src/rules/error-handling/TryScopeRule.d.ts +5 -0
  68. package/dist/src/rules/error-handling/TryScopeRule.d.ts.map +1 -1
  69. package/dist/src/rules/error-handling/TryScopeRule.js +30 -7
  70. package/dist/src/rules/error-handling/TryScopeRule.js.map +1 -1
  71. package/dist/src/rules/http/ConnectionIdleTimeoutRule.d.ts +27 -0
  72. package/dist/src/rules/http/ConnectionIdleTimeoutRule.d.ts.map +1 -0
  73. package/dist/src/rules/http/ConnectionIdleTimeoutRule.js +46 -0
  74. package/dist/src/rules/http/ConnectionIdleTimeoutRule.js.map +1 -0
  75. package/dist/src/rules/index.d.ts +1 -1
  76. package/dist/src/rules/index.d.ts.map +1 -1
  77. package/dist/src/rules/index.js +50 -8
  78. package/dist/src/rules/index.js.map +1 -1
  79. package/dist/src/rules/logging/LoggerPayloadRule.d.ts +15 -0
  80. package/dist/src/rules/logging/LoggerPayloadRule.d.ts.map +1 -1
  81. package/dist/src/rules/logging/LoggerPayloadRule.js +48 -4
  82. package/dist/src/rules/logging/LoggerPayloadRule.js.map +1 -1
  83. package/dist/src/rules/operations/FlowRefTargetExistsRule.d.ts +23 -0
  84. package/dist/src/rules/operations/FlowRefTargetExistsRule.d.ts.map +1 -0
  85. package/dist/src/rules/operations/FlowRefTargetExistsRule.js +58 -0
  86. package/dist/src/rules/operations/FlowRefTargetExistsRule.js.map +1 -0
  87. package/dist/src/rules/operations/UnusedFlowRule.d.ts +20 -0
  88. package/dist/src/rules/operations/UnusedFlowRule.d.ts.map +1 -1
  89. package/dist/src/rules/operations/UnusedFlowRule.js +73 -7
  90. package/dist/src/rules/operations/UnusedFlowRule.js.map +1 -1
  91. package/dist/src/rules/operations/UnusedVariableRule.d.ts +31 -0
  92. package/dist/src/rules/operations/UnusedVariableRule.d.ts.map +1 -0
  93. package/dist/src/rules/operations/UnusedVariableRule.js +103 -0
  94. package/dist/src/rules/operations/UnusedVariableRule.js.map +1 -0
  95. package/dist/src/rules/performance/ListenerReconnectForeverRule.d.ts +28 -0
  96. package/dist/src/rules/performance/ListenerReconnectForeverRule.d.ts.map +1 -0
  97. package/dist/src/rules/performance/ListenerReconnectForeverRule.js +56 -0
  98. package/dist/src/rules/performance/ListenerReconnectForeverRule.js.map +1 -0
  99. package/dist/src/rules/performance/ReconnectionStrategyRule.d.ts +7 -4
  100. package/dist/src/rules/performance/ReconnectionStrategyRule.d.ts.map +1 -1
  101. package/dist/src/rules/performance/ReconnectionStrategyRule.js +44 -24
  102. package/dist/src/rules/performance/ReconnectionStrategyRule.js.map +1 -1
  103. package/dist/src/rules/security/ConnectorCredentialsSecuredRule.d.ts +36 -0
  104. package/dist/src/rules/security/ConnectorCredentialsSecuredRule.d.ts.map +1 -0
  105. package/dist/src/rules/security/ConnectorCredentialsSecuredRule.js +124 -0
  106. package/dist/src/rules/security/ConnectorCredentialsSecuredRule.js.map +1 -0
  107. package/dist/src/rules/security/HardcodedCredentialsRule.d.ts +4 -0
  108. package/dist/src/rules/security/HardcodedCredentialsRule.d.ts.map +1 -1
  109. package/dist/src/rules/security/HardcodedCredentialsRule.js +15 -0
  110. package/dist/src/rules/security/HardcodedCredentialsRule.js.map +1 -1
  111. package/dist/src/rules/security/SecurePropertiesEncryptionRule.d.ts +25 -0
  112. package/dist/src/rules/security/SecurePropertiesEncryptionRule.d.ts.map +1 -0
  113. package/dist/src/rules/security/SecurePropertiesEncryptionRule.js +59 -0
  114. package/dist/src/rules/security/SecurePropertiesEncryptionRule.js.map +1 -0
  115. package/dist/src/rules/security/SecurePropertiesKeyRule.d.ts +23 -0
  116. package/dist/src/rules/security/SecurePropertiesKeyRule.d.ts.map +1 -0
  117. package/dist/src/rules/security/SecurePropertiesKeyRule.js +45 -0
  118. package/dist/src/rules/security/SecurePropertiesKeyRule.js.map +1 -0
  119. package/dist/src/rules/security/TlsKeystorePasswordRule.d.ts +25 -0
  120. package/dist/src/rules/security/TlsKeystorePasswordRule.d.ts.map +1 -0
  121. package/dist/src/rules/security/TlsKeystorePasswordRule.js +63 -0
  122. package/dist/src/rules/security/TlsKeystorePasswordRule.js.map +1 -0
  123. package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.d.ts +26 -0
  124. package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.d.ts.map +1 -0
  125. package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.js +61 -0
  126. package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.js.map +1 -0
  127. package/dist/src/rules/standards/ConfigPropertiesOrderingRule.d.ts +34 -0
  128. package/dist/src/rules/standards/ConfigPropertiesOrderingRule.d.ts.map +1 -0
  129. package/dist/src/rules/standards/ConfigPropertiesOrderingRule.js +76 -0
  130. package/dist/src/rules/standards/ConfigPropertiesOrderingRule.js.map +1 -0
  131. package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.d.ts +25 -0
  132. package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.d.ts.map +1 -0
  133. package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.js +111 -0
  134. package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.js.map +1 -0
  135. package/dist/src/rules/yaml/YamlRules.d.ts +6 -2
  136. package/dist/src/rules/yaml/YamlRules.d.ts.map +1 -1
  137. package/dist/src/rules/yaml/YamlRules.js +15 -11
  138. package/dist/src/rules/yaml/YamlRules.js.map +1 -1
  139. package/dist/src/types/Rule.d.ts +13 -0
  140. package/dist/src/types/Rule.d.ts.map +1 -1
  141. package/docs/README.md +87 -27
  142. package/docs/best-practices/ci-cd.md +135 -0
  143. package/docs/best-practices/connector-patterns.md +253 -0
  144. package/docs/best-practices/dataweave-patterns.md +370 -0
  145. package/docs/best-practices/deployment-2026.md +171 -0
  146. package/docs/best-practices/error-handling.md +277 -0
  147. package/docs/best-practices/event-driven-patterns.md +424 -0
  148. package/docs/best-practices/logging.md +163 -0
  149. package/docs/best-practices/mulesoft-best-practices.md +72 -865
  150. package/docs/best-practices/performance.md +273 -0
  151. package/docs/best-practices/rules-catalog.md +337 -29
  152. package/docs/best-practices/security.md +181 -0
  153. package/docs/best-practices/testing.md +190 -0
  154. package/docs/best-practices/variable-contracts.md +191 -0
  155. package/docs/linter/architecture.md +119 -64
  156. package/package.json +1 -1
@@ -0,0 +1,190 @@
1
+ # Testing with MUnit
2
+
3
+ > **Applies to:** All
4
+ > **Related Rules:** `EXP-003`
5
+ > **Last Updated:** April 2026
6
+
7
+ ## When to Read This
8
+
9
+ Read this when writing MUnit tests, setting up test infrastructure, or testing event-driven flows.
10
+
11
+ ---
12
+
13
+ ## Test Coverage Goals
14
+
15
+ | Test Type | Coverage Target | Purpose |
16
+ | -------------------- | ------------------ | ------------------------------ |
17
+ | Unit Tests | 80%+ flow coverage | Validate individual flow logic |
18
+ | Integration Tests | All critical paths | Validate end-to-end scenarios |
19
+ | Error Scenario Tests | All error handlers | Validate error responses |
20
+
21
+ ---
22
+
23
+ ## Patterns
24
+
25
+ ### Pattern 1: Standard MUnit Test Structure
26
+
27
+ ```xml
28
+ <munit:test name="create-order-success-test"
29
+ description="Validates successful order creation">
30
+
31
+ <!-- Mock external dependencies -->
32
+ <munit:behavior>
33
+ <munit-tools:mock-when processor="http:request">
34
+ <munit-tools:with-attributes>
35
+ <munit-tools:with-attribute attributeName="config-ref"
36
+ whereValue="API_HTTP_Config"/>
37
+ </munit-tools:with-attributes>
38
+ <munit-tools:then-return>
39
+ <munit-tools:payload value='{"orderId": "12345"}'/>
40
+ </munit-tools:then-return>
41
+ </munit-tools:mock-when>
42
+ </munit:behavior>
43
+
44
+ <!-- Execute the flow -->
45
+ <munit:execution>
46
+ <flow-ref name="create-order-flow"/>
47
+ </munit:execution>
48
+
49
+ <!-- Assert results -->
50
+ <munit:validation>
51
+ <munit-tools:assert-that expression="#[payload.orderId]"
52
+ is="#[MunitTools::notNullValue()]"/>
53
+ </munit:validation>
54
+ </munit:test>
55
+ ```
56
+
57
+ ### Pattern 2: Testing Error Handlers
58
+
59
+ ```xml
60
+ <munit:test name="error-handler-400-test"
61
+ description="Validates 400 Bad Request error handling"
62
+ expectedErrorType="APIKIT:BAD_REQUEST">
63
+
64
+ <munit:behavior>
65
+ <munit-tools:mock-when processor="apikit:router">
66
+ <munit-tools:then-call exception="APIKIT:BAD_REQUEST"/>
67
+ </munit-tools:mock-when>
68
+ </munit:behavior>
69
+
70
+ <munit:execution>
71
+ <flow-ref name="api-main"/>
72
+ </munit:execution>
73
+
74
+ <munit:validation>
75
+ <munit-tools:assert-that expression="#[vars.httpStatus]"
76
+ is="#[MunitTools::equalTo(400)]"/>
77
+ <munit-tools:assert-that expression="#[payload.error]"
78
+ is="#[MunitTools::equalTo('InvalidInput')]"/>
79
+ </munit:validation>
80
+ </munit:test>
81
+ ```
82
+
83
+ ### Pattern 3: Testing Event-Driven Flows
84
+
85
+ For PAPI-style flows driven by Platform Events, mock the event payload and downstream SAPI calls:
86
+
87
+ ```xml
88
+ <munit:test name="account-event-processing-test"
89
+ description="Validates Account event → NS Customer sync">
90
+
91
+ <munit:behavior>
92
+ <!-- Mock the SAPI HTTP call -->
93
+ <munit-tools:mock-when processor="http:request">
94
+ <munit-tools:with-attributes>
95
+ <munit-tools:with-attribute attributeName="config-ref"
96
+ whereValue="SAPI_HTTP_Config"/>
97
+ </munit-tools:with-attributes>
98
+ <munit-tools:then-return>
99
+ <munit-tools:payload value='{"status":"success","internalId":"123"}'/>
100
+ </munit-tools:then-return>
101
+ </munit-tools:mock-when>
102
+
103
+ <!-- Mock the writeback SAPI call -->
104
+ <munit-tools:mock-when processor="http:request">
105
+ <munit-tools:with-attributes>
106
+ <munit-tools:with-attribute attributeName="doc:name"
107
+ whereValue="Writeback Request"/>
108
+ </munit-tools:with-attributes>
109
+ <munit-tools:then-return>
110
+ <munit-tools:payload value='{"success":true}'/>
111
+ </munit-tools:then-return>
112
+ </munit-tools:mock-when>
113
+ </munit:behavior>
114
+
115
+ <munit:execution>
116
+ <!-- Set variables as the event listener would -->
117
+ <set-variable variableName="correlationId" value="test-uuid-123"/>
118
+ <set-variable variableName="logCategory" value="com.myorg.papi"/>
119
+ <set-variable variableName="salesforceId" value="001XXXXX"/>
120
+ <set-payload value='#[readUrl("classpath://test-data/account-event.json", "application/json")]'/>
121
+ <flow-ref name="account-process-subflow"/>
122
+ </munit:execution>
123
+
124
+ <munit:validation>
125
+ <munit-tools:assert-that expression="#[payload.status]"
126
+ is="#[MunitTools::equalTo('success')]"/>
127
+ </munit:validation>
128
+ </munit:test>
129
+ ```
130
+
131
+ ### Pattern 4: Test Organization
132
+
133
+ ```
134
+ src/test/
135
+ ├── munit/
136
+ │ ├── salesforce-upsert-test.xml # Operation-specific tests
137
+ │ ├── salesforce-create-test.xml
138
+ │ ├── salesforce-query-test.xml
139
+ │ ├── salesforce-delete-test.xml
140
+ │ ├── salesforce-process-subflow-test.xml # Routing tests
141
+ │ ├── error-handling-test.xml # Error scenario tests
142
+ │ └── common-test-resources.xml # Shared mocks
143
+ └── resources/
144
+ ├── log4j2-test.xml # Console-only, noise-suppressed
145
+ └── test-data/ # Test payloads
146
+ ├── account-event.json
147
+ └── order-request.json
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Key Principles
153
+
154
+ 1. **Mock all external dependencies** — never call real systems in unit tests
155
+ 2. **Test all error handler branches** — verify each HTTP status code / error type
156
+ 3. **Use descriptive test names** — names should describe the scenario being tested
157
+ 4. **Isolate tests** — each test should be independent (no shared state)
158
+ 5. **Separate test log config** — use `log4j2-test.xml` with console appender and suppressed noise
159
+
160
+ ---
161
+
162
+ ## Running Tests
163
+
164
+ ```bash
165
+ # Full test suite
166
+ mvn clean test -Dmule.env=dev -Dsecure.key=test
167
+
168
+ # Specific test suite
169
+ mvn test -Dmule.env=dev -Dsecure.key=test -Dtest=salesforce-upsert-test
170
+
171
+ # Package (skip tests)
172
+ mvn clean package -DskipTests
173
+ ```
174
+
175
+ > **Note:** MUnit requires MuleSoft Enterprise Edition license. If the `licm` check fails in your dev environment, verify with `mvn process-classes` (schema validation passes without EE license).
176
+
177
+ ---
178
+
179
+ ## Checklist
180
+
181
+ - [ ] 80%+ flow coverage with MUnit
182
+ - [ ] All error handler branches tested
183
+ - [ ] External dependencies mocked (HTTP, connectors, databases)
184
+ - [ ] Test names clearly describe the scenario
185
+ - [ ] `log4j2-test.xml` configured for test environment
186
+ - [ ] Test data externalized to `test-data/` directory
187
+
188
+ ---
189
+
190
+ **See also:** [Error Handling](error-handling.md) · [CI/CD Integration](ci-cd.md)
@@ -0,0 +1,191 @@
1
+ # Variable Contracts & Correlation ID Patterns
2
+
3
+ > **Applies to:** All
4
+ > **Related Rules:** `MULE-007` · `MULE-102` · `STD-001`
5
+ > **Last Updated:** April 2026
6
+
7
+ ## When to Read This
8
+
9
+ Read this when designing the variable contract between flows and sub-flows. Standardized variables ensure consistent behavior across routing, logging, error handling, and response building.
10
+
11
+ ---
12
+
13
+ ## Standard Variable Set
14
+
15
+ Every APIKit route flow or event listener flow should set these variables **before** delegating to sub-flows:
16
+
17
+ | Variable | Type | Required | Description |
18
+ | ----------------- | ------- | ------------------ | --------------------------------------------------------------- |
19
+ | `correlationId` | String | ✅ Always | Unique request/event identifier for distributed tracing |
20
+ | `logCategory` | String | ✅ Always | Logger category (e.g., `com.myorg.sf.sapi`) |
21
+ | `flowName` | String | ✅ Always | Human-readable flow identifier for logging |
22
+ | `env` | String | ✅ Always | Current environment from `p('mule.env')` |
23
+ | `action` | String | ✅ For CRUD APIs | Operation type: `CREATE`, `UPDATE`, `UPSERT`, `DELETE`, `QUERY` |
24
+ | `sObjectType` | String | ✅ For entity APIs | Entity/record type from URI parameter |
25
+ | `isArray` | Boolean | ✅ For CRUD APIs | Whether the original request payload was an array |
26
+ | `externalIdField` | String | Optional | External ID field for upsert operations (defaults to `Id`) |
27
+
28
+ ---
29
+
30
+ ## Patterns
31
+
32
+ ### Pattern 1: APIKit Route Variable Setup
33
+
34
+ **Use when:** setting variables in an APIKit-generated route flow.
35
+
36
+ Set all variables in a single `ee:transform` at the top of every route flow:
37
+
38
+ ```xml
39
+ <flow name="post:\salesforce\(sObjectType):application\json:api-config">
40
+ <ee:transform doc:name="Set Variables">
41
+ <ee:variables>
42
+ <ee:set-variable variableName="correlationId"><![CDATA[
43
+ attributes.headers.'x-correlation-id' default correlationId
44
+ ]]></ee:set-variable>
45
+ <ee:set-variable variableName="logCategory"><![CDATA[
46
+ "com.myorg.sf.sapi"
47
+ ]]></ee:set-variable>
48
+ <ee:set-variable variableName="flowName"><![CDATA[
49
+ "Mulesoft: my-sapi - " ++ flow.name
50
+ ]]></ee:set-variable>
51
+ <ee:set-variable variableName="env"><![CDATA[
52
+ p('mule.env')
53
+ ]]></ee:set-variable>
54
+ <ee:set-variable variableName="action"><![CDATA[
55
+ "CREATE"
56
+ ]]></ee:set-variable>
57
+ <ee:set-variable variableName="sObjectType"><![CDATA[
58
+ attributes.uriParams.'sObjectType'
59
+ ]]></ee:set-variable>
60
+ <ee:set-variable variableName="isArray"><![CDATA[
61
+ payload is Array
62
+ ]]></ee:set-variable>
63
+ </ee:variables>
64
+ </ee:transform>
65
+ <logger level="INFO" category="#[vars.logCategory]"
66
+ message='#["[" ++ vars.correlationId ++ "] CREATE for: " ++ vars.sObjectType]'/>
67
+ <flow-ref name="process-subflow"/>
68
+ </flow>
69
+ ```
70
+
71
+ ### Pattern 2: Event Listener Variable Setup
72
+
73
+ **Use when:** setting variables from a Salesforce Platform Event or Anypoint MQ message.
74
+
75
+ ```xml
76
+ <flow name="sf-account-event-listener">
77
+ <salesforce:replay-channel-listener channel="/event/Account_Event__e" .../>
78
+ <ee:transform doc:name="Set Variables">
79
+ <ee:variables>
80
+ <!-- Correlation ID from event metadata, NOT HTTP header -->
81
+ <ee:set-variable variableName="correlationId"><![CDATA[
82
+ payload.EventUuid default correlationId
83
+ ]]></ee:set-variable>
84
+ <ee:set-variable variableName="logCategory"><![CDATA[
85
+ "com.myorg.sf.papi"
86
+ ]]></ee:set-variable>
87
+ <ee:set-variable variableName="salesforceId"><![CDATA[
88
+ payload.Record_Id__c default ""
89
+ ]]></ee:set-variable>
90
+ <ee:set-variable variableName="entity"><![CDATA[
91
+ "account"
92
+ ]]></ee:set-variable>
93
+ </ee:variables>
94
+ </ee:transform>
95
+ <!-- ... -->
96
+ </flow>
97
+ ```
98
+
99
+ ### Pattern 3: Array-In / Array-Out Response Mirroring
100
+
101
+ **Use when:** an API accepts both single objects and arrays, and the response must mirror the input shape.
102
+
103
+ Set `vars.isArray` in the route flow:
104
+
105
+ ```xml
106
+ <ee:set-variable variableName="isArray"><![CDATA[
107
+ payload is Array
108
+ ]]></ee:set-variable>
109
+ ```
110
+
111
+ Use it in DWL response transforms to mirror the shape:
112
+
113
+ ```dataweave
114
+ %dw 2.0
115
+ output application/json
116
+ ---
117
+ if (vars.isArray)
118
+ payload map { id: $.id, success: $.success }
119
+ else
120
+ { id: payload[0].id, success: payload[0].success }
121
+ ```
122
+
123
+ ### Pattern 4: Action-Based Routing
124
+
125
+ **Use when:** a single process sub-flow handles multiple CRUD operations.
126
+
127
+ The `vars.action` variable drives routing inside the process sub-flow:
128
+
129
+ ```xml
130
+ <sub-flow name="salesforce-process-subflow">
131
+ <choice>
132
+ <when expression="#[vars.action == 'CREATE']">
133
+ <flow-ref name="salesforce-create-subflow"/>
134
+ </when>
135
+ <when expression="#[vars.action == 'UPSERT']">
136
+ <flow-ref name="salesforce-upsert-subflow"/>
137
+ </when>
138
+ <when expression="#[vars.action == 'DELETE']">
139
+ <flow-ref name="salesforce-delete-subflow"/>
140
+ </when>
141
+ <when expression="#[vars.action == 'QUERY' or vars.action == 'QUERY_ENTITY']">
142
+ <flow-ref name="salesforce-query-subflow"/>
143
+ </when>
144
+ </choice>
145
+ </sub-flow>
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Correlation ID Patterns
151
+
152
+ | Source | Pattern | Use Case |
153
+ | -------------- | ------------------------------------------------------------- | ---------------------------- |
154
+ | HTTP header | `attributes.headers.'x-correlation-id' default correlationId` | HTTP APIs (SAPI, Experience) |
155
+ | Platform Event | `payload.EventUuid default correlationId` | Event-driven PAPI |
156
+ | Anypoint MQ | `attributes.messageId default correlationId` | MQ consumers |
157
+ | Generated | `correlationId` (Mule built-in) | Fallback / internal flows |
158
+
159
+ **Propagation rules:**
160
+
161
+ - Include in all outbound HTTP requests as `x-correlation-id` header
162
+ - Include in all log messages: `#["[" ++ vars.correlationId ++ "] message"]`
163
+ - Include in all error response payloads
164
+ - Store in `vars.correlationId` (camelCase)
165
+
166
+ ---
167
+
168
+ ## Variable Naming Conventions
169
+
170
+ | Convention | Example | Use For |
171
+ | ------------------- | ----------------------------------------- | -------------------------- |
172
+ | `camelCase` | `correlationId`, `sObjectType`, `isArray` | All `set-variable` names |
173
+ | Static strings | `"com.myorg.sf.sapi"` | Logger categories, actions |
174
+ | Property references | `p('mule.env')` | Environment-derived values |
175
+
176
+ > ❌ **Don't use:** `snake_case` (`correlation_id`), `PascalCase` (`CorrelationId`), or `SCREAMING_CASE` (`CORRELATION_ID`) for variable names.
177
+
178
+ ---
179
+
180
+ ## Checklist
181
+
182
+ - [ ] All route flows set `correlationId`, `logCategory`, `flowName`, `env` before delegation
183
+ - [ ] CRUD route flows additionally set `action`, `sObjectType`, `isArray`
184
+ - [ ] Correlation ID sourced correctly for project type (HTTP vs. event vs. MQ)
185
+ - [ ] `correlationId` propagated in all outbound requests, logs, and error responses
186
+ - [ ] Variables use `camelCase` naming
187
+ - [ ] `isArray` set before calling process sub-flow (for response mirroring)
188
+
189
+ ---
190
+
191
+ **See also:** [Error Handling](error-handling.md) · [Logging](logging.md) · [Event-Driven Patterns](event-driven-patterns.md)
@@ -17,21 +17,23 @@ flowchart TB
17
17
  E --> D
18
18
  end
19
19
 
20
- subgraph Rules["Rules (56 Total)"]
21
- D --> R1[Error Handling<br/>MULE-001,003,005,007,009]
20
+ subgraph Rules["Rules (82 Total)"]
21
+ D --> R1[Error Handling<br/>MULE-001,003,005,007,009<br/>ERR-001,002,003,004]
22
22
  D --> R2[Naming<br/>MULE-002,101,102]
23
- D --> R3[Security<br/>MULE-004,201,202]
24
- D --> R4[Logging<br/>MULE-006,301,303]
25
- D --> R5[HTTP<br/>MULE-401,402,403]
26
- D --> R6[Performance<br/>MULE-501,502,503]
27
- D --> R7[Documentation<br/>MULE-601,604]
28
- D --> R8[Standards<br/>MULE-008,010,701]
23
+ D --> R3[Security<br/>MULE-004,201,202<br/>SEC-002,003,004,006,007,008,009,010]
24
+ D --> R4[Logging<br/>MULE-006,301,303<br/>LOG-001,004,HYG-001]
25
+ D --> R5[HTTP<br/>MULE-401,402,403,HTTP-004]
26
+ D --> R6[Performance<br/>MULE-501,502,503<br/>PERF-002,RES-001,002]
27
+ D --> R7[Documentation<br/>MULE-601,604,DOC-001]
28
+ D --> R8[Standards<br/>MULE-008,010,701<br/>OPS,CFG,STD]
29
29
  D --> R9[Complexity<br/>MULE-801]
30
30
  D --> R10[Structure<br/>MULE-802-804]
31
31
  D --> R11[YAML<br/>YAML-001,003,004]
32
- D --> R12[DataWeave<br/>DW-001,002,003]
33
- D --> R13[API-Led<br/>API-001,002,003]
34
- D --> R14[Experimental<br/>EXP-001,002,003]
32
+ D --> R12[DataWeave<br/>DW-001,002,003,004,005]
33
+ D --> R13[API-Led<br/>API-001-008]
34
+ D --> R14[Connectors<br/>SF-001,002]
35
+ D --> R15[Governance<br/>PROJ-001,002]
36
+ D --> R16[Experimental<br/>EXP-001,002,003]
35
37
  end
36
38
 
37
39
  subgraph Output["Formatters"]
@@ -64,16 +66,28 @@ sequenceDiagram
64
66
  Engine->>Scanner: scanDirectory(path)
65
67
  Scanner-->>Engine: ScannedFile[]
66
68
 
67
- loop Each XML File
69
+ Note over Engine: Pre-Scan Phase
70
+ loop Each XML File (Pre-Scan)
68
71
  Engine->>Parser: parseXml(content)
69
- Parser-->>Engine: Document
72
+ Parser-->>Engine: Document (cached)
73
+ Note over Engine: Collect allFlowRefs, allFlowNames
74
+ end
75
+ Note over Engine: Detect projectLayer
70
76
 
71
- loop Each Rule
77
+ loop Each XML File
78
+ Engine->>Engine: Get Document from cache
79
+ loop Each Per-File Rule
72
80
  Engine->>Rules: validate(doc, context)
73
81
  Rules-->>Engine: Issue[]
74
82
  end
75
83
  end
76
84
 
85
+ Note over Engine: Project Rules
86
+ loop Each Project Rule
87
+ Engine->>Rules: validateProject(context)
88
+ Rules-->>Engine: Issue[]
89
+ end
90
+
77
91
  loop YAML Rules
78
92
  Engine->>YAML: parseYaml(path)
79
93
  YAML-->>Engine: Properties
@@ -91,15 +105,36 @@ sequenceDiagram
91
105
  The central orchestrator that:
92
106
 
93
107
  1. Scans directories for XML and YAML files using FileScanner
94
- 2. Parses each file with XmlParser or YamlParser
95
- 3. Executes all enabled rules against each document
96
- 4. Aggregates results into a LintReport
108
+ 2. **Pre-scans** all XML files to collect cross-file metadata (`allFlowRefs`, `allFlowNames`, `projectContext` with `projectLayer`)
109
+ 3. **Caches** parsed XML `Document` objects to avoid redundant parsing
110
+ 4. Executes all enabled per-file rules against each cached document
111
+ 5. Executes project-level rules (`ProjectRule` subclasses) once per scan
112
+ 6. Aggregates results into a LintReport
97
113
 
98
114
  ```typescript
99
115
  const engine = new LintEngine({ rules: ALL_RULES, config });
100
116
  const report = await engine.scan('./project');
101
117
  ```
102
118
 
119
+ #### Document Cache
120
+
121
+ During `preScanFiles()`, the engine parses each XML file and stores the resulting `Document` in an internal `Map<string, Document>`. When `processFile()` runs, it retrieves the cached document instead of re-parsing. The cache is cleared after each scan to free memory.
122
+
123
+ #### Project Layer Detection
124
+
125
+ The engine automatically classifies projects into a `ProjectLayer`:
126
+
127
+ | Layer | Detection Heuristic |
128
+ | --------- | ----------------------------------------------------------- |
129
+ | `sapi` | Directory name contains `-sapi`, `-sys-`, or `-system-` |
130
+ | `papi` | Directory name contains `-papi`, `-proc-`, or `-process-` |
131
+ | `eapi` | Directory name contains `-eapi`, `-exp-`, or `-experience-` |
132
+ | `library` | Directory name contains `-library`, `-lib`, or `-common` |
133
+ | `batch` | Batch job elements detected in XML files |
134
+ | `unknown` | Default when no pattern matches |
135
+
136
+ Available to rules via `context.projectContext?.projectLayer`.
137
+
103
138
  ### XPathHelper
104
139
 
105
140
  Singleton utility for namespace-aware XPath queries:
@@ -111,20 +146,24 @@ const flows = xpath.selectNodes('//mule:flow', document);
111
146
 
112
147
  Pre-configured namespaces:
113
148
 
114
- | Prefix | Namespace |
115
- | -------- | ------------------------------------------------- |
116
- | `mule` | http://www.mulesoft.org/schema/mule/core |
117
- | `http` | http://www.mulesoft.org/schema/mule/http |
118
- | `ee` | http://www.mulesoft.org/schema/mule/ee/core |
119
- | `db` | http://www.mulesoft.org/schema/mule/db |
120
- | `doc` | http://www.mulesoft.org/schema/mule/documentation |
121
- | `tls` | http://www.mulesoft.org/schema/mule/tls |
122
- | `file` | http://www.mulesoft.org/schema/mule/file |
123
- | `sftp` | http://www.mulesoft.org/schema/mule/sftp |
124
- | `vm` | http://www.mulesoft.org/schema/mule/vm |
125
- | `jms` | http://www.mulesoft.org/schema/mule/jms |
126
- | `apikit` | http://www.mulesoft.org/schema/mule/mule-apikit |
127
- | `batch` | http://www.mulesoft.org/schema/mule/batch |
149
+ | Prefix | Namespace |
150
+ | ------------- | ------------------------------------------------- |
151
+ | `mule` | http://www.mulesoft.org/schema/mule/core |
152
+ | `http` | http://www.mulesoft.org/schema/mule/http |
153
+ | `ee` | http://www.mulesoft.org/schema/mule/ee/core |
154
+ | `db` | http://www.mulesoft.org/schema/mule/db |
155
+ | `doc` | http://www.mulesoft.org/schema/mule/documentation |
156
+ | `tls` | http://www.mulesoft.org/schema/mule/tls |
157
+ | `file` | http://www.mulesoft.org/schema/mule/file |
158
+ | `sftp` | http://www.mulesoft.org/schema/mule/sftp |
159
+ | `vm` | http://www.mulesoft.org/schema/mule/vm |
160
+ | `jms` | http://www.mulesoft.org/schema/mule/jms |
161
+ | `apikit` | http://www.mulesoft.org/schema/mule/mule-apikit |
162
+ | `batch` | http://www.mulesoft.org/schema/mule/batch |
163
+ | `netsuite` | http://www.mulesoft.org/schema/mule/netsuite |
164
+ | `sap` | http://www.mulesoft.org/schema/mule/sap |
165
+ | `anypoint-mq` | http://www.mulesoft.org/schema/mule/anypoint-mq |
166
+ | `oauth` | http://www.mulesoft.org/schema/mule/oauth |
128
167
 
129
168
  ### BaseRule
130
169
 
@@ -145,6 +184,11 @@ classDiagram
145
184
  #getOption(context, key, default): T
146
185
  }
147
186
 
187
+ class ProjectRule {
188
+ +validateProject(context): Issue[]
189
+ +validate(doc, context): Issue[]
190
+ }
191
+
148
192
  class FlowNamingRule {
149
193
  +validate()
150
194
  }
@@ -154,8 +198,14 @@ classDiagram
154
198
  #findYamlFiles(): string[]
155
199
  }
156
200
 
201
+ class GlobalErrorHandlerRule {
202
+ +validateProject()
203
+ }
204
+
157
205
  BaseRule <|-- FlowNamingRule
158
206
  BaseRule <|-- YamlRuleBase
207
+ BaseRule <|-- ProjectRule
208
+ ProjectRule <|-- GlobalErrorHandlerRule
159
209
  ```
160
210
 
161
211
  **Issue Types for Quality Metrics:**
@@ -212,11 +262,11 @@ XPathHelper.getInstance(); // Same instance always
212
262
  src/
213
263
  ├── index.ts # Package entry point
214
264
  ├── types/ # TypeScript interfaces
215
- │ ├── Rule.ts # Rule, Issue, Severity, IssueType
265
+ │ ├── Rule.ts # Rule, Issue, Severity, IssueType, ProjectLayer
216
266
  │ ├── Report.ts # LintReport, FileResult
217
267
  │ └── Config.ts # LintConfig, CliOptions
218
268
  ├── core/ # Core utilities
219
- │ ├── XPathHelper.ts # Namespace-aware XPath
269
+ │ ├── XPathHelper.ts # Namespace-aware XPath (16 namespaces)
220
270
  │ ├── XmlParser.ts # DOM parsing
221
271
  │ ├── YamlParser.ts # YAML parsing
222
272
  │ ├── FileScanner.ts # File discovery
@@ -228,23 +278,26 @@ src/
228
278
  │ ├── thresholds.ts # A-E rating boundaries
229
279
  │ └── calculator.ts # Rating calculation functions
230
280
  ├── engine/ # Orchestration
231
- │ └── LintEngine.ts # Main engine
232
- ├── rules/ # All rules (56 total)
233
- │ ├── index.ts # Rule registry
234
- │ ├── base/ # BaseRule class
235
- │ ├── api-led/ # API-001, 002, 003, 004
281
+ │ └── LintEngine.ts # Main engine (document cache, pre-scan, project layer)
282
+ ├── rules/ # All rules (82 total)
283
+ │ ├── index.ts # Rule registry (ALL_RULES array)
284
+ │ ├── base/ # BaseRule + ProjectRule classes
285
+ │ ├── api-led/ # API-001–004, API-006–008
236
286
  │ ├── complexity/ # MULE-801
237
- │ ├── dataweave/ # DW-001, 002, 003, 004
238
- │ ├── documentation/ # MULE-601, 604
239
- │ ├── error-handling/ # MULE-001, 003, 005, 007, 009 (issueType: bug)
240
- │ ├── experimental/ # EXP-001, 002, 003
241
- │ ├── http/ # MULE-401, 402, 403
242
- │ ├── logging/ # MULE-006, 301, 303
287
+ │ ├── connector/ # SF-001, SF-002
288
+ │ ├── dataweave/ # DW-001–005
289
+ │ ├── documentation/ # MULE-601, 604, DOC-001
290
+ │ ├── error-handling/ # MULE-001,003,005,007,009, ERR-001–004
291
+ │ ├── experimental/ # EXP-001–003
292
+ │ ├── governance/ # PROJ-001, PROJ-002
293
+ │ ├── http/ # MULE-401–403, HTTP-004
294
+ │ ├── logging/ # MULE-006,301,303, LOG-001,004, HYG-001
243
295
  │ ├── naming/ # MULE-002, 101, 102
244
- │ ├── performance/ # MULE-501, 502, 503
245
- │ ├── security/ # MULE-004, 201, 202 (issueType: vulnerability)
246
- │ ├── standards/ # MULE-008, 010, 701
247
- │ ├── structure/ # MULE-802, 803, 804
296
+ │ ├── operations/ # HYG-002–005
297
+ │ ├── performance/ # MULE-501–503, PERF-002, RES-001–002
298
+ │ ├── security/ # MULE-004,201,202, SEC-002–004,006–010
299
+ │ ├── standards/ # MULE-008,010,701, OPS-001–003, API-005, CFG-001–002, STD-001
300
+ │ ├── structure/ # MULE-802–804
248
301
  │ └── yaml/ # YAML-001, 003, 004
249
302
  └── formatters/ # Output formatters
250
303
  ├── TableFormatter.ts
@@ -262,22 +315,24 @@ src/
262
315
 
263
316
  ## Rule Categories
264
317
 
265
- | Category | ID Prefix | Count | Description |
266
- | -------------- | ------------------ | ----- | ---------------------------------------- |
267
- | Error Handling | MULE-00X | 5 | Error handler presence and configuration |
268
- | Naming | MULE-002, 10X | 3 | Flow, variable, and file naming |
269
- | Security | MULE-004, 20X | 3 | Hardcoded values and security |
270
- | Logging | MULE-006, 30X | 3 | Logger configuration |
271
- | HTTP | MULE-40X | 3 | HTTP request configuration |
272
- | Performance | MULE-50X | 3 | Performance anti-patterns |
273
- | Documentation | MULE-60X | 2 | Component documentation |
274
- | Standards | MULE-008, 010, 70X | 3 | Best practices |
275
- | Complexity | MULE-80X | 1 | Cyclomatic complexity |
276
- | Structure | MULE-80X | 3 | Project structure |
277
- | YAML | YAML-XXX | 3 | Properties validation |
278
- | DataWeave | DW-XXX | 3 | DWL file validation |
279
- | API-Led | API-XXX | 3 | API-Led patterns |
280
- | Experimental | EXP-XXX | 3 | Beta rules |
318
+ | Category | ID Prefix | Count | Description |
319
+ | -------------- | ------------------------------- | ----- | ---------------------------------------------- |
320
+ | Error Handling | MULE-00X, ERR-001–004 | 9 | Error handler configuration and best practices |
321
+ | Naming | MULE-002, 10X | 3 | Flow, variable, and file naming |
322
+ | Security | MULE-004, 20X, SEC-002–010 | 11 | Hardcoded values, TLS, credentials |
323
+ | Logging | MULE-006, 30X, LOG, HYG-001 | 6 | Logger configuration and hygiene |
324
+ | HTTP | MULE-40X, HTTP-004 | 4 | HTTP request configuration |
325
+ | Performance | MULE-50X, PERF-002, RES-001–002 | 6 | Performance anti-patterns and resilience |
326
+ | Documentation | MULE-60X, DOC-001 | 3 | Component documentation |
327
+ | Standards | MULE-008,010,70X, OPS, CFG, STD | 10 | Best practices and operations |
328
+ | Complexity | MULE-801 | 1 | Cyclomatic complexity |
329
+ | Structure | MULE-80X | 3 | Project structure |
330
+ | YAML | YAML-XXX | 3 | Properties validation |
331
+ | DataWeave | DW-XXX | 5 | DWL file validation |
332
+ | API-Led | API-XXX | 7 | API-Led patterns |
333
+ | Connectors | SF-001, SF-002 | 2 | Salesforce and event connector rules |
334
+ | Governance | PROJ-XXX | 2 | POM and Git hygiene |
335
+ | Experimental | EXP-XXX | 3 | Beta rules |
281
336
 
282
337
  ## Extension Points
283
338
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfdxy/mule-lint",
3
- "version": "1.20.0",
3
+ "version": "1.22.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",