@flow-scanner/lightning-flow-scanner-core 6.14.0 → 6.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +62 -54
  2. package/main/interfaces/IRuleConfig.d.ts +2 -0
  3. package/main/interfaces/IRuleDefinition.d.ts +1 -0
  4. package/main/libs/ExportDetails.js +4 -1
  5. package/main/libs/ExportSarif.js +5 -5
  6. package/main/libs/RuleDocumentation.d.ts +20 -0
  7. package/main/libs/RuleDocumentation.js +41 -0
  8. package/main/libs/ScanFlows.js +11 -0
  9. package/main/models/FlatViolation.d.ts +3 -0
  10. package/main/models/RuleCommon.d.ts +1 -0
  11. package/main/models/RuleCommon.js +2 -0
  12. package/main/models/RuleInfo.d.ts +4 -0
  13. package/main/models/RuleInfo.js +3 -0
  14. package/main/models/RuleResult.d.ts +3 -0
  15. package/main/models/RuleResult.js +4 -0
  16. package/main/rules/APIVersion.js +2 -1
  17. package/main/rules/ActionCallsInLoop.js +3 -2
  18. package/main/rules/AutoLayout.js +2 -1
  19. package/main/rules/CopyAPIName.js +2 -1
  20. package/main/rules/CyclomaticComplexity.js +2 -1
  21. package/main/rules/DMLStatementInLoop.js +2 -1
  22. package/main/rules/DuplicateDMLOperation.js +2 -1
  23. package/main/rules/FlowDescription.js +2 -1
  24. package/main/rules/FlowName.js +2 -1
  25. package/main/rules/GetRecordAllFields.js +2 -1
  26. package/main/rules/HardcodedId.js +2 -1
  27. package/main/rules/HardcodedUrl.js +2 -1
  28. package/main/rules/InactiveFlow.js +2 -1
  29. package/main/rules/MissingFaultPath.js +2 -1
  30. package/main/rules/MissingMetadataDescription.js +3 -2
  31. package/main/rules/MissingNullHandler.js +2 -1
  32. package/main/rules/MissingRecordTriggerFilter.js +2 -1
  33. package/main/rules/ProcessBuilder.js +4 -1
  34. package/main/rules/RecordIdAsString.js +3 -2
  35. package/main/rules/RecursiveAfterUpdate.js +2 -1
  36. package/main/rules/SOQLQueryInLoop.js +2 -1
  37. package/main/rules/SameRecordFieldUpdates.js +2 -1
  38. package/main/rules/TransformInsteadOfLoop.js +2 -1
  39. package/main/rules/TriggerOrder.js +2 -1
  40. package/main/rules/UnconnectedElement.js +2 -1
  41. package/main/rules/UnsafeRunningContext.js +2 -1
  42. package/main/rules/UnusedVariable.js +2 -1
  43. package/package.json +1 -1
package/README.md CHANGED
@@ -33,10 +33,10 @@
33
33
 
34
34
  - **[Default Rules](#default-rules)**
35
35
  - **[Configuration](#configuration)**
36
- - [Configure Severity Levels](#configure-severity-levels)
36
+ - [Configure Rules](#configure-rules)
37
37
  - [Overwrite Expressions](#overwrite-expressions)
38
38
  - [Define Exceptions](#define-exceptions)
39
- - [Exclude Flows from Scanning](#exclude-flows-from-scanning)
39
+ - [Exclude Flows](#exclude-flows)
40
40
  - [Scan Modes](#scan-modes)
41
41
  - **[Installation](#installation)**
42
42
  - [Distributions](#distributions)
@@ -55,189 +55,189 @@
55
55
 
56
56
  <!-- START GENERATED_RULES -->
57
57
  ### Action Call In A Loop
58
- To prevent exceeding Apex governor limits, it is advisable to consolidate and bulkify your apex calls, utilizing a single action call containing a collection variable at the end of the loop.
58
+ Repeatedly invoking Apex actions inside a loop can exhaust governor limits and lead to performance issues. Where possible, bulkify your logic by moving the action call outside the loop and passing a collection variable instead.
59
59
 
60
60
  **Rule ID:** `action-call-in-loop`
61
61
  **Class Name:** _[ActionCallsInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/ActionCallsInLoop.ts)_
62
- **Severity:** 🔴 *Error*
62
+ **Severity:** 🟡 *Warning*
63
63
 
64
64
  ### DML Statement In A Loop
65
- To prevent exceeding Apex governor limits, consolidate all your database operations—record creation, updates, or deletions—at the conclusion of the flow.
65
+ Executing DML operations (insert, update, delete) inside a loop is a high-risk anti-pattern that frequently causes governor limit exceptions. All database operations should be collected and executed once, outside the loop.
66
66
 
67
67
  **Rule ID:** `dml-in-loop`
68
68
  **Class Name:** _[DMLStatementInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/DMLStatementInLoop.ts)_
69
69
  **Severity:** 🔴 *Error*
70
70
 
71
71
  ### Duplicate DML Operation
72
- When a flow executes database changes or actions between two screens, prevent users from navigating backward between screens; otherwise, duplicate database operations may be performed.
72
+ When a Flow performs database operations across multiple screens, users navigating backward can cause the same actions to run multiple times. To prevent unintended changes, either restrict backward navigation or redesign the Flow so database operations execute in a single, forward-moving step.
73
73
 
74
74
  **Rule ID:** `duplicate-dml`
75
75
  **Class Name:** _[DuplicateDMLOperation](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/DuplicateDMLOperation.ts)_
76
76
  **Severity:** 🟡 *Warning*
77
77
 
78
78
  ### Excessive Cyclomatic Complexity
79
- The number of loops and decision rules, plus the number of decisions. Use a combination of 1) subflows and 2) breaking flows into multiple concise trigger ordered flows, to reduce the cyclomatic complexity within a single flow, ensuring maintainability and simplicity.
79
+ High numbers of loops and decision elements increase a Flow’s cyclomatic complexity. To maintain simplicity and readability, consider using subflows or splitting a Flow into smaller, ordered Flows.
80
80
 
81
81
  **Rule ID:** `excessive-cyclomatic-complexity`
82
82
  **Class Name:** _[CyclomaticComplexity](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/CyclomaticComplexity.ts)_
83
83
  **Severity:** 🔵 *Note*
84
84
 
85
85
  ### Flow Naming Convention
86
- The readability of a flow is paramount. Establishing a naming convention significantly enhances findability, searchability, and overall consistency. Include at least a domain and a brief description of the flow’s actions, for example `Service_OrderFulfillment`.
86
+ Using clear and consistent Flow names improves readability, discoverability, and maintainability. A good naming convention helps team members quickly understand a Flow’s purpose—for example, including a domain and brief description like Service_OrderFulfillment. Adopt a naming pattern that aligns with your organization’s standards.
87
87
 
88
88
  **Rule ID:** `invalid-naming-convention`
89
89
  **Class Name:** _[FlowName](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/FlowName.ts)_
90
90
  **Severity:** 🔴 *Error*
91
91
 
92
92
  ### Get Record All Fields
93
- Following the principle of least privilege (PoLP), avoid using **Get Records** with “Automatically store all fields” unless necessary.
93
+ Avoid using Get Records to retrieve all fields unless necessary. This improves performance, reduces processing time, and limits exposure of unnecessary data.
94
94
 
95
95
  **Rule ID:** `get-record-all-fields`
96
96
  **Class Name:** _[GetRecordAllFields](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/GetRecordAllFields.ts)_
97
97
  **Severity:** 🟡 *Warning*
98
98
 
99
99
  ### Hardcoded Id
100
- Avoid hard-coding IDs because they are org specific. Instead, pass them into variables at the start of the flowvia merge-field URL parameters or a **Get Records** element.
100
+ Avoid hard-coding record IDs, as they are unique to a specific org and will not work in other environments. Instead, store IDs in variablessuch as merge-field URL parameters or a **Get Records** element—to make the Flow portable, maintainable, and flexible.
101
101
 
102
102
  **Rule ID:** `hardcoded-id`
103
103
  **Class Name:** _[HardcodedId](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/HardcodedId.ts)_
104
104
  **Severity:** 🔴 *Error*
105
105
 
106
106
  ### Hardcoded Url
107
- Avoid hard-coding URLs because they are environment specific. Use an `$API` formula (preferred) or environment-specific sources like custom labels, metadata, or settings.
107
+ Avoid hard-coding URLs, as they may change between environments or over time. Instead, store URLs in variables or custom settings to make the Flow adaptable, maintainable, and environment-independent.
108
108
 
109
109
  **Rule ID:** `hardcoded-url`
110
110
  **Class Name:** _[HardcodedUrl](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/HardcodedUrl.ts)_
111
111
  **Severity:** 🔴 *Error*
112
112
 
113
113
  ### Inactive Flow
114
- Like cleaning out your closet: deleting unused flows is essential. Inactive flows can still cause trouble—such as accidentally deleting records during testing, or being activated as subflows.
114
+ Inactive Flows should be deleted or archived to reduce risk. Even when inactive, they can cause unintended record changes during testing or be activated as subflows. Keeping only active, relevant Flows improves safety and maintainability.
115
115
 
116
116
  **Rule ID:** `inactive-flow`
117
117
  **Class Name:** _[InactiveFlow](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/InactiveFlow.ts)_
118
118
  **Severity:** 🟡 *Warning*
119
119
 
120
120
  ### Invalid API Version
121
- Introducing newer API components may lead to unexpected issues with older versions of Flows, as they might not align with the underlying mechanics. Starting from API version 50.0, the **Api Version** attribute has been readily available on the Flow Object. To ensure smooth operation and reduce discrepancies between API versions, it is strongly advised to regularly update and maintain them.
121
+ Flows running on outdated API versions may behave inconsistently when newer platform features or components are used. From API version 50.0 onward, the API Version attribute explicitly controls Flow runtime behavior. Keeping Flows aligned with a supported API version helps prevent compatibility issues and ensures predictable execution.
122
122
 
123
123
  **Rule ID:** `invalid-api-version`
124
124
  **Class Name:** _[APIVersion](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/APIVersion.ts)_
125
125
  **Severity:** 🟡 *Warning*
126
126
 
127
127
  ### Missing Auto Layout
128
- With Canvas Mode set to Auto-Layout, elements are spaced, connected, and aligned automatically, keeping your Flow neatly organized—saving you time.
128
+ Auto-Layout automatically arranges and aligns Flow elements, keeping the canvas organized and easier to maintain. Enabling it saves time and improves readability.
129
129
 
130
130
  **Rule ID:** `missing-auto-layout`
131
131
  **Class Name:** _[AutoLayout](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/AutoLayout.ts)_
132
132
  **Severity:** 🔵 *Note*
133
133
 
134
134
  ### Missing Fault Path
135
- A flow may fail to execute an operation as intended. By default, the flow displays an error to the user and emails the creator. Customize this behavior by incorporating a Fault Path.
135
+ Elements that can fail should include a Fault Path to handle errors gracefully. Without it, failures show generic errors to users. Fault Paths improve reliability and user experience.
136
136
 
137
137
  **Rule ID:** `missing-fault-path`
138
138
  **Class Name:** _[MissingFaultPath](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingFaultPath.ts)_
139
139
  **Severity:** 🟡 *Warning*
140
140
 
141
141
  ### Missing Filter Record Trigger ![Beta](https://img.shields.io/badge/status-beta-yellow)
142
- Record-triggered flows that lack filters on changed fields or entry conditions can lead to unnecessary executions on every record change. This may degrade system performance, hit governor limits faster, and increase resource consumption in high-volume orgs.
142
+ Record-triggered Flows without filters on changed fields or entry conditions execute on every record change. Adding filters ensures the Flow runs only when needed, improving performance.
143
143
 
144
144
  **Rule ID:** `missing-record-trigger-filter`
145
145
  **Class Name:** _[MissingFilterRecordTrigger](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingFilterRecordTrigger.ts)_
146
146
  **Severity:** 🟡 *Warning*
147
147
 
148
148
  ### Missing Flow Description
149
- Descriptions play a vital role in documentation. It is highly recommended to include details about where a flow is used and its intended purpose.
149
+ Flow descriptions are essential for documentation and maintainability. Include a description for each Flow, explaining its purpose and where it’s used.
150
150
 
151
151
  **Rule ID:** `missing-flow-description`
152
152
  **Class Name:** _[FlowDescription](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/FlowDescription.ts)_
153
153
  **Severity:** 🔴 *Error*
154
154
 
155
155
  ### Missing Metadata Description ![Beta](https://img.shields.io/badge/status-beta-yellow)
156
- Flags Flow elements (Get Records, Assignments, Decisions, Actions, etc.) and metadata components (Variables, Formulas, Constants, Text Templates) that lack a description. Adding concise descriptions greatly improves readability, maintainability, and helps AI tools understand your automation intent.
156
+ Elements and metadata without a description reduce clarity and maintainability. Adding descriptions improves readability and makes your automation easier to understand.
157
157
 
158
158
  **Rule ID:** `missing-metadata-description`
159
159
  **Class Name:** _[MissingMetadataDescription](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingMetadataDescription.ts)_
160
- **Severity:** 🔴 *Error*
160
+ **Severity:** 🟡 *Warning*
161
161
 
162
162
  ### Missing Null Handler
163
- When a **Get Records** operation finds no data, it returns `null`. Validate data by using a Decision element to check for a non-null result.
163
+ Get Records operations return null when no data is found. Without handling these null values, Flows can fail or produce unintended results. Adding a null check improves reliability and ensures the Flow behaves as expected.
164
164
 
165
165
  **Rule ID:** `missing-null-handler`
166
166
  **Class Name:** _[MissingNullHandler](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingNullHandler.ts)_
167
167
  **Severity:** 🟡 *Warning*
168
168
 
169
169
  ### Missing Trigger Order
170
- Guarantee your flow execution order with the **Trigger Order** property introduced in Spring '22.value to their flows and guarantee their execution order. This priority value is not an absolute value, so the values need not be sequentially numbered as 1, 2, 3, and so on.
170
+ Record-triggered Flows without a specified Trigger Order may execute in an unpredictable sequence. Setting a Trigger Order ensures your Flows run in the intended order.
171
171
 
172
172
  **Rule ID:** `unspecified-trigger-order`
173
173
  **Class Name:** _[TriggerOrder](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/TriggerOrder.ts)_
174
174
  **Severity:** 🔵 *Note*
175
175
 
176
176
  ### Process Builder
177
- Salesforce is transitioning away from Workflow Rules and Process Builder in favor of Flow. Begin migrating your organization’s automation to Flow.
177
+ Process Builder is retired. Continuing to use it increases maintenance overhead and risks future compatibility issues. Migrating automation to Flow reduces risk and improves maintainability.
178
178
 
179
179
  **Rule ID:** `process-builder-usage`
180
180
  **Class Name:** _[ProcessBuilder](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/ProcessBuilder.ts)_
181
- **Severity:** 🟡 *Warning*
181
+ **Severity:** 🔴 *Error*
182
182
 
183
183
  ### Record ID as String ![Beta](https://img.shields.io/badge/status-beta-yellow)
184
- Detects flows using a String variable named `recordId` as input when they could receive the entire record object instead. Since recent Salesforce releases, record pages and quick actions can pass the complete record, eliminating the need for an additional Get Records query and improving performance.
184
+ Flows that use a String variable for a record ID instead of receiving the full record introduce unnecessary complexity and additional Get Records queries. Using the complete record simplifies the Flow and improves performance.
185
185
 
186
186
  **Rule ID:** `record-id-as-string`
187
187
  **Class Name:** _[RecordIdAsString](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/RecordIdAsString.ts)_
188
- **Severity:** 🔴 *Error*
188
+ **Severity:** 🔵 *Note*
189
189
 
190
190
  ### Recursive After Update
191
- After-update flows are meant for modifying **other** records. Using them on the same record can cause recursion. Consider **before-save** flows for same-record updates.
191
+ After-save Flows that update the same record can trigger recursion, causing unintended behavior or performance issues. Avoid updating the triggering record in after-save Flows; use before-save Flows instead to prevent recursion.
192
192
 
193
193
  **Rule ID:** `recursive-record-update`
194
194
  **Class Name:** _[RecursiveAfterUpdate](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/RecursiveAfterUpdate.ts)_
195
195
  **Severity:** 🟡 *Warning*
196
196
 
197
197
  ### Same Record Field Updates
198
- Similar to triggers, **before-save** contexts can update the same record via `$Record` without invoking DML.
198
+ Before-save Flows can safely update the triggering record directly via $Record, applying changes efficiently without extra DML operations. Using before-save updates improves performance
199
199
 
200
200
  **Rule ID:** `same-record-field-updates`
201
201
  **Class Name:** _[SameRecordFieldUpdates](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/SameRecordFieldUpdates.ts)_
202
202
  **Severity:** 🟡 *Warning*
203
203
 
204
204
  ### SOQL Query In A Loop
205
- To prevent exceeding Apex governor limits, consolidate all SOQL queries at the end of the flow.
205
+ Running SOQL queries inside a loop can rapidly exceed query limits and severely degrade performance. Queries should be executed once, with results reused throughout the loop.
206
206
 
207
207
  **Rule ID:** `soql-in-loop`
208
208
  **Class Name:** _[SOQLQueryInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/SOQLQueryInLoop.ts)_
209
209
  **Severity:** 🔴 *Error*
210
210
 
211
211
  ### Transform Instead of Loop ![Beta](https://img.shields.io/badge/status-beta-yellow)
212
- Detects Loop elements that directly connect to Assignment elements. Transform elements handle collection manipulation in bulk operations, providing significant performance improvements over iterative loop-assignment patterns.
212
+ Loop elements that perform direct Assignments on each item can slow down Flows. Using Transform elements allows bulk operations on collections, improving performance and reducing complexity.
213
213
 
214
214
  **Rule ID:** `transform-instead-of-loop`
215
215
  **Class Name:** _[TransformInsteadOfLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/TransformInsteadOfLoop.ts)_
216
216
  **Severity:** 🔵 *Note*
217
217
 
218
218
  ### Unclear API Name
219
- Maintaining multiple elements with a similar name, like `Copy_X_Of_Element`, can diminish the overall readability of your Flow. When copying and pasting these elements, remember to update the API name of the newly created copy.
219
+ Elements with unclear or duplicated API names, like Copy_X_Of_Element, reduce Flow readability. Make sure to update the API name when copying elements to keep your Flow organized.
220
220
 
221
221
  **Rule ID:** `unclear-api-naming`
222
222
  **Class Name:** _[CopyAPIName](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/CopyAPIName.ts)_
223
223
  **Severity:** 🟡 *Warning*
224
224
 
225
225
  ### Unreachable Element
226
- Avoid unconnected elements that are not used by the flow to keep flows efficient and maintainable.
226
+ Unconnected elements never execute and add unnecessary clutter. Remove or connect unused Flow elements to keep Flows clean and efficient.
227
227
 
228
228
  **Rule ID:** `unreachable-element`
229
229
  **Class Name:** _[UnconnectedElement](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/UnconnectedElement.ts)_
230
230
  **Severity:** 🟡 *Warning*
231
231
 
232
232
  ### Unsafe Running Context
233
- This flow is configured to run in System Mode without Sharing. This system context grants all running users the permission to view and edit all data in your org. Running a flow in System Mode without Sharing can lead to unsafe data access.
233
+ Flows configured to run in System Mode without Sharing grant access to all data, bypassing user permissions. Avoid this setting to prevent security risks and protect sensitive data.
234
234
 
235
235
  **Rule ID:** `unsafe-running-context`
236
236
  **Class Name:** _[UnsafeRunningContext](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/UnsafeRunningContext.ts)_
237
237
  **Severity:** 🔴 *Error*
238
238
 
239
239
  ### Unused Variable
240
- To maintain efficiency and manageability, avoid including variables that are never referenced.
240
+ Unused variables are never referenced and add unnecessary clutter. Remove them to keep Flows efficient and easy to maintain.
241
241
 
242
242
  **Rule ID:** `unused-variable`
243
243
  **Class Name:** _[UnusedVariable](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/UnusedVariable.ts)_
@@ -253,21 +253,22 @@ It is recommend to configure and define:
253
253
  - The severity of violating any specific rule.
254
254
  - Expressions used for rules, such as REGEX patterns and comparison operators.
255
255
  - Any known exceptions that should be ignored during scanning.
256
- - Flows or directories to exclude from scanning entirely.
257
256
 
258
257
  ```json
259
258
  {
260
259
  "rules": {
261
- // Your rules here
260
+ // Your rule configurations
262
261
  },
263
262
  "exceptions": {
264
- // Your exceptions here
263
+ // Your defined exceptions
265
264
  }
266
265
  }
267
266
  ```
268
267
 
269
268
  Most Lightning Flow Scanner distributions automatically resolve configurations from `.flow-scanner.yml`, `.flow-scanner.json`, or `package.json` → `flowScanner`.
270
269
 
270
+ ### Configure Rules
271
+
271
272
  By default, all default rules are executed. You can customize individual rules and override the rules to be executed without having to specify every rule. Below is a breakdown of the available attributes of rule configuration:
272
273
 
273
274
  ```json
@@ -276,24 +277,37 @@ By default, all default rules are executed. You can customize individual rules a
276
277
  "<RuleId>": {
277
278
  "severity": "<Severity>", // Override severity level
278
279
  "expression": "<Expression>", // Override rule expression
279
- "enabled": "false" // Disable this rule
280
+ "message": "<Message>", // Set custom message
281
+ "messageUrl": "<URL>", // Set custom documentation URL
282
+ "enabled": false // Disable this rule
280
283
  }
281
284
  }
282
285
  }
283
286
  ```
284
287
 
285
- ### Configure Severity Levels
286
-
287
- When the severity is not provided it will be `warning` by default. Other available values for severity are `error` and `note`. Configure the severity per rule as shown below:
288
+ When the severity is not provided it will be `warning` by default. Other available values for severity are `error` and `note`. Configure the severity per rule as demonstrated below:
288
289
 
289
290
  ```json
290
291
  {
291
292
  "rules": {
292
- "missing-flow-description": {
293
- "severity": "error"
293
+ "record-id-as-string": {
294
+ "severity": "warning",
294
295
  },
295
- "unused-variable": {
296
- "severity": "note"
296
+ "unclear-api-naming": {
297
+ "severity": "error",
298
+ }
299
+ }
300
+ }
301
+ ```
302
+
303
+ If not provided, `message` shows the standard rule summary and `messageUrl` links to the README; providing either overrides the default behavior.
304
+
305
+ ```json
306
+ {
307
+ "rules": {
308
+ "dml-in-loop": {
309
+ "message": "Avoid DML inside loops. Bulkify operations instead.",
310
+ "messageUrl": "https://internal.docs.company.com/salesforce/flow-dml-best-practices"
297
311
  }
298
312
  }
299
313
  }
@@ -301,13 +315,13 @@ When the severity is not provided it will be `warning` by default. Other availab
301
315
 
302
316
  ### Overwrite Expressions
303
317
 
304
- Some rules have an expression to configure, such as the expression, that will overwrite default values. These can be configured in the same way as severity as shown in the following example.
318
+ Some rules are configurable and allow overriding their default expressions. You configure these overrides the same way as severity, as shown in the examples below.
305
319
 
306
320
  ```json
307
321
  {
308
322
  "rules": {
309
323
  "invalid-api-version": {
310
- "expression": "===58" // comparison operator
324
+ "expression": "===58" // comparison expression
311
325
  },
312
326
  "invalid-naming-convention": {
313
327
  "expression": "[A-Za-z0-9]" // regular expression
@@ -348,13 +362,11 @@ _Example_
348
362
  }
349
363
  ```
350
364
 
351
- ### Exclude Flows from Scanning
352
-
353
- Lightning Flow Scanner provides two complementary ways to exclude flows from scanning:
365
+ ### Exclude Flows
354
366
 
355
367
  #### Exclude by File Path (Node.js only)
356
368
 
357
- Use glob patterns to exclude flows based on their file system location. This is useful for excluding entire directories or specific file patterns during the flow discovery phase:
369
+ Use glob patterns to exclude flows based on their file system location. This is useful for excluding entire directories or specific name patterns:
358
370
 
359
371
  ```json
360
372
  {
@@ -365,8 +377,6 @@ Use glob patterns to exclude flows based on their file system location. This is
365
377
  }
366
378
  ```
367
379
 
368
- **Note**: The `ignore` option uses glob patterns and applies during file discovery before flows are parsed. This is the most efficient way to exclude large numbers of flows.
369
-
370
380
  **Environment compatibility**: `ignore` requires Node.js (file system access) and is available in CLI Plugin, VS Code Extension, and GitHub Action. It is **not** available when using the Core Library in browser/web environments.
371
381
 
372
382
  #### Exclude by Flow API Name (Browser-compatible)
@@ -386,8 +396,6 @@ Exclude specific flows by their unique API names, regardless of their location.
386
396
  }
387
397
  ```
388
398
 
389
- **Note**: The `ignoreFlows` option applies after flows are parsed, using the flow's API name (the `<fullName>` element in the flow metadata). Flow names are unique identifiers and work regardless of the flow's file system location.
390
-
391
399
  **Environment compatibility**: `ignoreFlows` works in **all environments** including Node.js and browser/web distributions, as it operates on parsed flow data rather than file system paths.
392
400
 
393
401
  ### Scan Modes
@@ -1,4 +1,6 @@
1
1
  export interface IRuleConfig {
2
2
  enabled?: boolean;
3
3
  severity?: "error" | "warning" | "note";
4
+ message?: string;
5
+ messageUrl?: string;
4
6
  }
@@ -2,6 +2,7 @@ import { Flow, RuleResult } from "../internals/internals";
2
2
  export interface IRuleDefinition {
3
3
  ruleId: string;
4
4
  description: string;
5
+ summary: string;
5
6
  docRefs: Array<{
6
7
  label: string;
7
8
  path: string;
@@ -106,8 +106,11 @@ function exportDetails(results, includeDetails = false) {
106
106
  const exported = _object_spread_props(_object_spread({}, base), {
107
107
  flowFile,
108
108
  flowName,
109
+ ruleId: rule.ruleId,
109
110
  ruleName: rule.ruleName,
110
- severity: (_rule_severity = rule.severity) !== null && _rule_severity !== void 0 ? _rule_severity : "warning"
111
+ severity: (_rule_severity = rule.severity) !== null && _rule_severity !== void 0 ? _rule_severity : "warning",
112
+ message: rule.message || rule.ruleDefinition.description,
113
+ messageUrl: rule.messageUrl
111
114
  });
112
115
  // Flatten details object into top-level properties if includeDetails is true
113
116
  if (includeDetails && details) {
@@ -63,14 +63,14 @@ function exportSarif(results) {
63
63
  }
64
64
  ],
65
65
  message: {
66
- text: r.errorMessage || `${r.ruleName} in ${d.name}`
66
+ text: r.errorMessage || (r.message || r.ruleDefinition.description ? `${r.message || r.ruleDefinition.description} (${d.name})` : `${r.ruleId} in ${d.name}`)
67
67
  },
68
68
  properties: _object_spread({
69
69
  element: d.name,
70
70
  flow: flow.name,
71
71
  type: d.type
72
72
  }, d.details),
73
- ruleId: r.ruleName
73
+ ruleId: r.ruleId
74
74
  }))),
75
75
  tool: {
76
76
  driver: {
@@ -81,11 +81,11 @@ function exportSarif(results) {
81
81
  level: mapSeverity(r.severity)
82
82
  },
83
83
  fullDescription: {
84
- text: r.ruleDefinition.description || ""
84
+ text: r.message || r.ruleDefinition.description || ""
85
85
  },
86
- id: r.ruleName,
86
+ id: r.ruleId,
87
87
  shortDescription: {
88
- text: r.ruleDefinition.description || r.ruleName
88
+ text: r.message || r.ruleDefinition.description || r.ruleId
89
89
  }
90
90
  })),
91
91
  version: "1.0.0"
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Converts a rule label to a documentation URL anchor.
3
+ *
4
+ * Rules:
5
+ * - Lowercase
6
+ * - Spaces → hyphens
7
+ * - Remove special characters and badges
8
+ *
9
+ * @param label - The rule display label (e.g., "DML Statement In A Loop")
10
+ * @returns The anchor slug (e.g., "dml-statement-in-a-loop")
11
+ */
12
+ export declare function labelToAnchor(label: string): string;
13
+ /**
14
+ * Generates a documentation URL for a rule.
15
+ *
16
+ * @param label - The rule display label
17
+ * @param customUrl - Optional custom URL override
18
+ * @returns The documentation URL
19
+ */
20
+ export declare function getRuleDocumentationUrl(label: string, customUrl?: string): string;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Converts a rule label to a documentation URL anchor.
3
+ *
4
+ * Rules:
5
+ * - Lowercase
6
+ * - Spaces → hyphens
7
+ * - Remove special characters and badges
8
+ *
9
+ * @param label - The rule display label (e.g., "DML Statement In A Loop")
10
+ * @returns The anchor slug (e.g., "dml-statement-in-a-loop")
11
+ */ "use strict";
12
+ Object.defineProperty(exports, "__esModule", {
13
+ value: true
14
+ });
15
+ function _export(target, all) {
16
+ for(var name in all)Object.defineProperty(target, name, {
17
+ enumerable: true,
18
+ get: Object.getOwnPropertyDescriptor(all, name).get
19
+ });
20
+ }
21
+ _export(exports, {
22
+ get getRuleDocumentationUrl () {
23
+ return getRuleDocumentationUrl;
24
+ },
25
+ get labelToAnchor () {
26
+ return labelToAnchor;
27
+ }
28
+ });
29
+ function labelToAnchor(label) {
30
+ return label.toLowerCase().replace(/\s+/g, '-') // Replace spaces with hyphens
31
+ .replace(/[^a-z0-9-]/g, '') // Remove non-alphanumeric except hyphens
32
+ .replace(/-+/g, '-') // Collapse multiple hyphens
33
+ .replace(/^-|-$/g, ''); // Trim leading/trailing hyphens
34
+ }
35
+ function getRuleDocumentationUrl(label, customUrl) {
36
+ if (customUrl) {
37
+ return customUrl;
38
+ }
39
+ const anchor = labelToAnchor(label);
40
+ return `https://flow-scanner.github.io/lightning-flow-scanner/#${anchor}`;
41
+ }
@@ -20,6 +20,7 @@ const _internals = require("../../main/internals/internals");
20
20
  const _IRulesConfig = require("../interfaces/IRulesConfig");
21
21
  const _Violation = require("../models/Violation");
22
22
  const _GetRuleDefinitions = require("./GetRuleDefinitions");
23
+ const _RuleDocumentation = require("./RuleDocumentation");
23
24
  function getRuleConfigByIdOrName(rule, rulesConfig) {
24
25
  if (!rulesConfig) return undefined;
25
26
  // Try ruleId first, then fall back to name
@@ -76,6 +77,16 @@ function ScanFlows(flows, ruleOptions) {
76
77
  const config = getRuleConfigByIdOrName(rule, ruleOptions === null || ruleOptions === void 0 ? void 0 : ruleOptions.rules);
77
78
  const suppressions = getSuppressionsForRule(rule, flow.name, ruleOptions === null || ruleOptions === void 0 ? void 0 : ruleOptions.exceptions);
78
79
  const result = config && Object.keys(config).length > 0 ? rule.execute(flow, config, suppressions) : rule.execute(flow, undefined, suppressions);
80
+ // Apply custom message if provided in config, otherwise use summary
81
+ if (config && typeof config === 'object' && 'message' in config && typeof config.message === 'string') {
82
+ result.message = config.message;
83
+ } else {
84
+ // Use summary if available, otherwise fallback to description
85
+ result.message = result.ruleDefinition.summary || result.ruleDefinition.description;
86
+ }
87
+ // Apply custom messageUrl if provided in config, otherwise auto-generate from rule label
88
+ const customUrl = config && typeof config === 'object' && 'messageUrl' in config && typeof config.messageUrl === 'string' ? config.messageUrl : undefined;
89
+ result.messageUrl = (0, _RuleDocumentation.getRuleDocumentationUrl)(result.ruleDefinition.label, customUrl);
79
90
  if (result.details.length > 0) {
80
91
  let flowXml = flowXmlCache.get(flow.name);
81
92
  if (!flowXml) {
@@ -2,8 +2,11 @@ import { Violation } from "./Violation";
2
2
  export interface FlatViolation extends Omit<Violation, 'details'> {
3
3
  flowFile: string;
4
4
  flowName: string;
5
+ ruleId: string;
5
6
  ruleName: string;
6
7
  severity: string;
8
+ message?: string;
9
+ messageUrl?: string;
7
10
  dataType?: string;
8
11
  locationX?: string;
9
12
  locationY?: string;
@@ -2,6 +2,7 @@ import { RuleInfo } from "./RuleInfo";
2
2
  import * as core from "../internals/internals";
3
3
  export declare abstract class RuleCommon {
4
4
  description: string;
5
+ summary: string;
5
6
  docRefs: Array<{
6
7
  label: string;
7
8
  path: string;
@@ -128,6 +128,7 @@ let RuleCommon = class RuleCommon {
128
128
  }
129
129
  constructor(info, optional){
130
130
  _define_property(this, "description", void 0);
131
+ _define_property(this, "summary", void 0);
131
132
  _define_property(this, "docRefs", []);
132
133
  _define_property(this, "isConfigurable", void 0);
133
134
  _define_property(this, "label", void 0);
@@ -141,6 +142,7 @@ let RuleCommon = class RuleCommon {
141
142
  this.supportedTypes = info.supportedTypes;
142
143
  this.label = info.label;
143
144
  this.description = info.description;
145
+ this.summary = info.summary;
144
146
  this.uri = `https://github.com/Lightning-Flow-Scanner/lightning-flow-scanner/tree/main/src/main/rules/${info.name}.ts`;
145
147
  this.docRefs = info.docRefs;
146
148
  const checkImpl = this.check;
@@ -11,6 +11,10 @@ export declare class RuleInfo {
11
11
  * A human-readable description of the rule.
12
12
  */
13
13
  description: string;
14
+ /**
15
+ * A short summary (5-10 words) used when no custom message is provided.
16
+ */
17
+ summary: string;
14
18
  /**
15
19
  * An array of documentation references related to the rule.
16
20
  */
@@ -27,6 +27,9 @@ let RuleInfo = class RuleInfo {
27
27
  * A human-readable description of the rule.
28
28
  */ _define_property(this, "description", void 0);
29
29
  /**
30
+ * A short summary (5-10 words) used when no custom message is provided.
31
+ */ _define_property(this, "summary", void 0);
32
+ /**
30
33
  * An array of documentation references related to the rule.
31
34
  */ _define_property(this, "docRefs", void 0);
32
35
  /**
@@ -3,9 +3,12 @@ import { Violation } from "./Violation";
3
3
  export declare class RuleResult {
4
4
  occurs: boolean;
5
5
  ruleName: string;
6
+ ruleId: string;
6
7
  ruleDefinition: IRuleDefinition;
7
8
  severity: string;
8
9
  details: Violation[];
9
10
  errorMessage: string;
11
+ message?: string;
12
+ messageUrl?: string;
10
13
  constructor(info: IRuleDefinition, details: Violation[], errorMessage?: string);
11
14
  }
@@ -25,12 +25,16 @@ let RuleResult = class RuleResult {
25
25
  constructor(info, details, errorMessage){
26
26
  _define_property(this, "occurs", void 0);
27
27
  _define_property(this, "ruleName", void 0);
28
+ _define_property(this, "ruleId", void 0);
28
29
  _define_property(this, "ruleDefinition", void 0);
29
30
  _define_property(this, "severity", void 0);
30
31
  _define_property(this, "details", []);
31
32
  _define_property(this, "errorMessage", void 0);
33
+ _define_property(this, "message", void 0); // Custom message that overrides the default rule description
34
+ _define_property(this, "messageUrl", void 0); // URL to custom documentation (fallback to rule docs if not provided)
32
35
  this.ruleDefinition = info;
33
36
  this.ruleName = info.name;
37
+ this.ruleId = info.ruleId;
34
38
  this.severity = info.severity ? info.severity : "warning";
35
39
  this.occurs = false;
36
40
  this.details = details;
@@ -116,7 +116,8 @@ let APIVersion = class APIVersion extends _RuleCommon.RuleCommon {
116
116
  ruleId: "invalid-api-version",
117
117
  name: "APIVersion",
118
118
  label: "Invalid API Version",
119
- description: "Introducing newer API components may lead to unexpected issues with older versions of Flows, as they might not align with the underlying mechanics. Starting from API version 50.0, the **Api Version** attribute has been readily available on the Flow Object. To ensure smooth operation and reduce discrepancies between API versions, it is strongly advised to regularly update and maintain them.",
119
+ description: "Flows running on outdated API versions may behave inconsistently when newer platform features or components are used. From API version 50.0 onward, the API Version attribute explicitly controls Flow runtime behavior. Keeping Flows aligned with a supported API version helps prevent compatibility issues and ensures predictable execution.",
120
+ summary: "Outdated API versions risk compatibility issues",
120
121
  supportedTypes: _internals.FlowType.allTypes(),
121
122
  docRefs: []
122
123
  });
@@ -20,7 +20,8 @@ let ActionCallsInLoop = class ActionCallsInLoop extends _LoopRuleCommon.LoopRule
20
20
  constructor(){
21
21
  super({
22
22
  ruleId: "action-call-in-loop",
23
- description: "To prevent exceeding Apex governor limits, it is advisable to consolidate and bulkify your apex calls, utilizing a single action call containing a collection variable at the end of the loop.",
23
+ description: "Repeatedly invoking Apex actions inside a loop can exhaust governor limits and lead to performance issues. Where possible, bulkify your logic by moving the action call outside the loop and passing a collection variable instead.",
24
+ summary: "Action calls inside loop risk governor limits",
24
25
  docRefs: [
25
26
  {
26
27
  label: "Action Call In A Loop",
@@ -31,7 +32,7 @@ let ActionCallsInLoop = class ActionCallsInLoop extends _LoopRuleCommon.LoopRule
31
32
  name: "ActionCallsInLoop",
32
33
  supportedTypes: _internals.FlowType.backEndTypes
33
34
  }, {
34
- severity: "error"
35
+ severity: "warning"
35
36
  });
36
37
  }
37
38
  };
@@ -68,7 +68,8 @@ let AutoLayout = class AutoLayout extends _RuleCommon.RuleCommon {
68
68
  ruleId: "missing-auto-layout",
69
69
  name: "AutoLayout",
70
70
  label: "Missing Auto Layout",
71
- description: "With Canvas Mode set to Auto-Layout, elements are spaced, connected, and aligned automatically, keeping your Flow neatly organized—saving you time.",
71
+ description: "Auto-Layout automatically arranges and aligns Flow elements, keeping the canvas organized and easier to maintain. Enabling it saves time and improves readability.",
72
+ summary: "Auto-Layout improves canvas organization and readability",
72
73
  supportedTypes: _internals.FlowType.allTypes(),
73
74
  docRefs: []
74
75
  }, {
@@ -62,7 +62,8 @@ let CopyAPIName = class CopyAPIName extends _RuleCommon.RuleCommon {
62
62
  ruleId: "unclear-api-naming",
63
63
  name: "CopyAPIName",
64
64
  label: "Unclear API Name",
65
- description: "Maintaining multiple elements with a similar name, like `Copy_X_Of_Element`, can diminish the overall readability of your Flow. When copying and pasting these elements, remember to update the API name of the newly created copy.",
65
+ description: "Elements with unclear or duplicated API names, like Copy_X_Of_Element, reduce Flow readability. Make sure to update the API name when copying elements to keep your Flow organized.",
66
+ summary: "Duplicated API names reduce Flow readability",
66
67
  supportedTypes: _internals.FlowType.allTypes(),
67
68
  docRefs: []
68
69
  });
@@ -90,7 +90,8 @@ let CyclomaticComplexity = class CyclomaticComplexity extends _RuleCommon.RuleCo
90
90
  ruleId: "excessive-cyclomatic-complexity",
91
91
  name: "CyclomaticComplexity",
92
92
  label: "Excessive Cyclomatic Complexity",
93
- description: `The number of loops and decision rules, plus the number of decisions. Use a combination of 1) subflows and 2) breaking flows into multiple concise trigger ordered flows, to reduce the cyclomatic complexity within a single flow, ensuring maintainability and simplicity.`,
93
+ description: "High numbers of loops and decision elements increase a Flow's cyclomatic complexity. To maintain simplicity and readability, consider using subflows or splitting a Flow into smaller, ordered Flows.",
94
+ summary: "Too many loops and decisions harm readability",
94
95
  supportedTypes: _internals.FlowType.backEndTypes,
95
96
  docRefs: [
96
97
  {
@@ -21,7 +21,8 @@ let DMLStatementInLoop = class DMLStatementInLoop extends _LoopRuleCommon.LoopRu
21
21
  constructor(){
22
22
  super({
23
23
  ruleId: "dml-in-loop",
24
- description: "To prevent exceeding Apex governor limits, consolidate all your database operations—record creation, updates, or deletions—at the conclusion of the flow.",
24
+ description: "Executing DML operations (insert, update, delete) inside a loop is a high-risk anti-pattern that frequently causes governor limit exceptions. All database operations should be collected and executed once, outside the loop.",
25
+ summary: "DML operations inside loop risk governor limits",
25
26
  docRefs: [
26
27
  {
27
28
  label: "Flow Best Practices",
@@ -97,7 +97,8 @@ let DuplicateDMLOperation = class DuplicateDMLOperation extends _RuleCommon.Rule
97
97
  ruleId: "duplicate-dml",
98
98
  name: "DuplicateDMLOperation",
99
99
  label: "Duplicate DML Operation",
100
- description: "When a flow executes database changes or actions between two screens, prevent users from navigating backward between screens; otherwise, duplicate database operations may be performed.",
100
+ description: "When a Flow performs database operations across multiple screens, users navigating backward can cause the same actions to run multiple times. To prevent unintended changes, either restrict backward navigation or redesign the Flow so database operations execute in a single, forward-moving step.",
101
+ summary: "DML across screens may execute multiple times",
101
102
  supportedTypes: _internals.FlowType.visualTypes,
102
103
  docRefs: []
103
104
  });
@@ -64,7 +64,8 @@ let FlowDescription = class FlowDescription extends _RuleCommon.RuleCommon {
64
64
  constructor(){
65
65
  super({
66
66
  ruleId: "missing-flow-description",
67
- description: "Descriptions play a vital role in documentation. It is highly recommended to include details about where a flow is used and its intended purpose.",
67
+ description: "Flow descriptions are essential for documentation and maintainability. Include a description for each Flow, explaining its purpose and where it's used.",
68
+ summary: "Flow descriptions improve documentation and maintainability",
68
69
  docRefs: [],
69
70
  label: "Missing Flow Description",
70
71
  name: "FlowDescription",
@@ -67,7 +67,8 @@ let FlowName = class FlowName extends _RuleCommon.RuleCommon {
67
67
  constructor(){
68
68
  super({
69
69
  ruleId: "invalid-naming-convention",
70
- description: "The readability of a flow is paramount. Establishing a naming convention significantly enhances findability, searchability, and overall consistency. Include at least a domain and a brief description of the flow’s actions, for example `Service_OrderFulfillment`.",
70
+ description: "Using clear and consistent Flow names improves readability, discoverability, and maintainability. A good naming convention helps team members quickly understand a Flow's purpose—for example, including a domain and brief description like Service_OrderFulfillment. Adopt a naming pattern that aligns with your organization's standards.",
71
+ summary: "Consistent naming improves Flow discoverability and maintainability",
71
72
  docRefs: [
72
73
  {
73
74
  label: "Naming your Flows is more critical than ever. By Stephen Church",
@@ -69,7 +69,8 @@ let GetRecordAllFields = class GetRecordAllFields extends _RuleCommon.RuleCommon
69
69
  constructor(){
70
70
  super({
71
71
  ruleId: "get-record-all-fields",
72
- description: "Following the principle of least privilege (PoLP), avoid using **Get Records** with “Automatically store all fields” unless necessary.",
72
+ description: "Avoid using Get Records to retrieve all fields unless necessary. This improves performance, reduces processing time, and limits exposure of unnecessary data.",
73
+ summary: "Retrieving all fields harms performance and security",
73
74
  docRefs: [
74
75
  {
75
76
  label: "Get Records Stores All Fields",
@@ -61,7 +61,8 @@ let HardcodedId = class HardcodedId extends _RuleCommon.RuleCommon {
61
61
  ruleId: "hardcoded-id",
62
62
  name: "HardcodedId",
63
63
  label: "Hardcoded Id",
64
- description: "Avoid hard-coding IDs because they are org specific. Instead, pass them into variables at the start of the flowvia merge-field URL parameters or a **Get Records** element.",
64
+ description: "Avoid hard-coding record IDs, as they are unique to a specific org and will not work in other environments. Instead, store IDs in variablessuch as merge-field URL parameters or a **Get Records** element—to make the Flow portable, maintainable, and flexible.",
65
+ summary: "Hardcoded IDs break portability across environments",
65
66
  supportedTypes: _internals.FlowType.allTypes(),
66
67
  docRefs: [
67
68
  {
@@ -19,7 +19,8 @@ let HardcodedUrl = class HardcodedUrl extends _RuleCommon.RuleCommon {
19
19
  constructor(){
20
20
  super({
21
21
  ruleId: "hardcoded-url",
22
- description: "Avoid hard-coding URLs because they are environment specific. Use an `$API` formula (preferred) or environment-specific sources like custom labels, metadata, or settings.",
22
+ description: "Avoid hard-coding URLs, as they may change between environments or over time. Instead, store URLs in variables or custom settings to make the Flow adaptable, maintainable, and environment-independent.",
23
+ summary: "Hardcoded URLs break across different environments",
23
24
  docRefs: [
24
25
  {
25
26
  label: "The Ultimate Guide to Salesforce Flow Best Practices",
@@ -65,7 +65,8 @@ let InactiveFlow = class InactiveFlow extends _RuleCommon.RuleCommon {
65
65
  ruleId: "inactive-flow",
66
66
  name: "InactiveFlow",
67
67
  label: "Inactive Flow",
68
- description: "Like cleaning out your closet: deleting unused flows is essential. Inactive flows can still cause trouble—such as accidentally deleting records during testing, or being activated as subflows.",
68
+ description: "Inactive Flows should be deleted or archived to reduce risk. Even when inactive, they can cause unintended record changes during testing or be activated as subflows. Keeping only active, relevant Flows improves safety and maintainability.",
69
+ summary: "Inactive Flows should be deleted or archived",
69
70
  supportedTypes: _internals.FlowType.allTypes(),
70
71
  docRefs: []
71
72
  });
@@ -127,7 +127,8 @@ let MissingFaultPath = class MissingFaultPath extends _RuleCommon.RuleCommon {
127
127
  constructor(){
128
128
  super({
129
129
  ruleId: "missing-fault-path",
130
- description: "A flow may fail to execute an operation as intended. By default, the flow displays an error to the user and emails the creator. Customize this behavior by incorporating a Fault Path.",
130
+ description: "Elements that can fail should include a Fault Path to handle errors gracefully. Without it, failures show generic errors to users. Fault Paths improve reliability and user experience.",
131
+ summary: "Fault Paths enable graceful error handling",
131
132
  docRefs: [
132
133
  {
133
134
  label: "Flow Best Practices",
@@ -66,13 +66,14 @@ let MissingMetadataDescription = class MissingMetadataDescription extends _RuleC
66
66
  constructor(){
67
67
  super({
68
68
  ruleId: "missing-metadata-description",
69
- description: "Flags Flow elements (Get Records, Assignments, Decisions, Actions, etc.) and metadata components (Variables, Formulas, Constants, Text Templates) that lack a description. Adding concise descriptions greatly improves readability, maintainability, and helps AI tools understand your automation intent.",
69
+ description: "Elements and metadata without a description reduce clarity and maintainability. Adding descriptions improves readability and makes your automation easier to understand.",
70
+ summary: "Element descriptions improve clarity and maintainability",
70
71
  docRefs: [],
71
72
  label: "Missing Metadata Description",
72
73
  name: "MissingMetadataDescription",
73
74
  supportedTypes: _internals.FlowType.allTypes()
74
75
  }, {
75
- severity: "error"
76
+ severity: "warning"
76
77
  });
77
78
  }
78
79
  };
@@ -138,7 +138,8 @@ let MissingNullHandler = class MissingNullHandler extends _RuleCommon.RuleCommon
138
138
  constructor(){
139
139
  super({
140
140
  ruleId: "missing-null-handler",
141
- description: "When a **Get Records** operation finds no data, it returns `null`. Validate data by using a Decision element to check for a non-null result.",
141
+ description: "Get Records operations return null when no data is found. Without handling these null values, Flows can fail or produce unintended results. Adding a null check improves reliability and ensures the Flow behaves as expected.",
142
+ summary: "Null checks prevent failures from missing records",
142
143
  docRefs: [],
143
144
  label: "Missing Null Handler",
144
145
  name: "MissingNullHandler",
@@ -80,7 +80,8 @@ let MissingRecordTriggerFilter = class MissingRecordTriggerFilter extends _RuleC
80
80
  ruleId: "missing-record-trigger-filter",
81
81
  name: "MissingRecordTriggerFilter",
82
82
  label: "Missing Filter Record Trigger",
83
- description: "Record-triggered flows that lack filters on changed fields or entry conditions can lead to unnecessary executions on every record change. This may degrade system performance, hit governor limits faster, and increase resource consumption in high-volume orgs.",
83
+ description: "Record-triggered Flows without filters on changed fields or entry conditions execute on every record change. Adding filters ensures the Flow runs only when needed, improving performance.",
84
+ summary: "Filters ensure Flows run only when needed",
84
85
  supportedTypes: [
85
86
  _internals.FlowType.autolaunchedType
86
87
  ],
@@ -62,7 +62,8 @@ let ProcessBuilder = class ProcessBuilder extends _RuleCommon.RuleCommon {
62
62
  ruleId: "process-builder-usage",
63
63
  name: "ProcessBuilder",
64
64
  label: "Process Builder",
65
- description: "Salesforce is transitioning away from Workflow Rules and Process Builder in favor of Flow. Begin migrating your organization’s automation to Flow.",
65
+ description: "Process Builder is retired. Continuing to use it increases maintenance overhead and risks future compatibility issues. Migrating automation to Flow reduces risk and improves maintainability.",
66
+ summary: "Process Builder is retired, migrate to Flow",
66
67
  supportedTypes: _internals.FlowType.processBuilder,
67
68
  docRefs: [
68
69
  {
@@ -70,6 +71,8 @@ let ProcessBuilder = class ProcessBuilder extends _RuleCommon.RuleCommon {
70
71
  path: "https://help.salesforce.com/s/articleView?id=000389396&type=1"
71
72
  }
72
73
  ]
74
+ }, {
75
+ severity: "error"
73
76
  });
74
77
  }
75
78
  };
@@ -76,7 +76,8 @@ let RecordIdAsString = class RecordIdAsString extends _RuleCommon.RuleCommon {
76
76
  ruleId: "record-id-as-string",
77
77
  name: "RecordIdAsString",
78
78
  label: "Record ID as String",
79
- description: "Detects flows using a String variable named `recordId` as input when they could receive the entire record object instead. Since recent Salesforce releases, record pages and quick actions can pass the complete record, eliminating the need for an additional Get Records query and improving performance.",
79
+ description: "Flows that use a String variable for a record ID instead of receiving the full record introduce unnecessary complexity and additional Get Records queries. Using the complete record simplifies the Flow and improves performance.",
80
+ summary: "String record IDs add complexity and queries",
80
81
  supportedTypes: [
81
82
  ..._internals.FlowType.visualTypes,
82
83
  _internals.FlowType.autolaunchedType
@@ -88,7 +89,7 @@ let RecordIdAsString = class RecordIdAsString extends _RuleCommon.RuleCommon {
88
89
  }
89
90
  ]
90
91
  }, {
91
- severity: "error"
92
+ severity: "note"
92
93
  });
93
94
  }
94
95
  };
@@ -105,7 +105,8 @@ let RecursiveAfterUpdate = class RecursiveAfterUpdate extends _RuleCommon.RuleCo
105
105
  constructor(){
106
106
  super({
107
107
  ruleId: "recursive-record-update",
108
- description: "After-update flows are meant for modifying **other** records. Using them on the same record can cause recursion. Consider **before-save** flows for same-record updates.",
108
+ description: "After-save Flows that update the same record can trigger recursion, causing unintended behavior or performance issues. Avoid updating the triggering record in after-save Flows; use before-save Flows instead to prevent recursion.",
109
+ summary: "After-save updates to same record trigger recursion",
109
110
  docRefs: [
110
111
  {
111
112
  label: "Learn about same record field updates",
@@ -19,7 +19,8 @@ let SOQLQueryInLoop = class SOQLQueryInLoop extends _LoopRuleCommon.LoopRuleComm
19
19
  constructor(){
20
20
  super({
21
21
  ruleId: "soql-in-loop",
22
- description: "To prevent exceeding Apex governor limits, consolidate all SOQL queries at the end of the flow.",
22
+ description: "Running SOQL queries inside a loop can rapidly exceed query limits and severely degrade performance. Queries should be executed once, with results reused throughout the loop.",
23
+ summary: "SOQL queries inside loop risk governor limits",
23
24
  docRefs: [
24
25
  {
25
26
  label: "Flow Best Practices",
@@ -89,7 +89,8 @@ let SameRecordFieldUpdates = class SameRecordFieldUpdates extends _RuleCommon.Ru
89
89
  ruleId: "same-record-field-updates",
90
90
  name: "SameRecordFieldUpdates",
91
91
  label: "Same Record Field Updates",
92
- description: "Similar to triggers, **before-save** contexts can update the same record via `$Record` without invoking DML.",
92
+ description: "Before-save Flows can safely update the triggering record directly via $Record, applying changes efficiently without extra DML operations. Using before-save updates improves performance",
93
+ summary: "Before-save Flows can update $Record directly",
93
94
  supportedTypes: [
94
95
  ..._internals.FlowType.backEndTypes
95
96
  ],
@@ -79,7 +79,8 @@ let TransformInsteadOfLoop = class TransformInsteadOfLoop extends _RuleCommon.Ru
79
79
  ruleId: "transform-instead-of-loop",
80
80
  name: "TransformInsteadOfLoop",
81
81
  label: "Transform Instead of Loop",
82
- description: "Detects Loop elements that directly connect to Assignment elements. Transform elements handle collection manipulation in bulk operations, providing significant performance improvements over iterative loop-assignment patterns.",
82
+ description: "Loop elements that perform direct Assignments on each item can slow down Flows. Using Transform elements allows bulk operations on collections, improving performance and reducing complexity.",
83
+ summary: "Transform elements enable faster bulk operations",
83
84
  supportedTypes: _internals.FlowType.allTypes(),
84
85
  docRefs: [
85
86
  {
@@ -71,7 +71,8 @@ let TriggerOrder = class TriggerOrder extends _RuleCommon.RuleCommon {
71
71
  ruleId: "unspecified-trigger-order",
72
72
  name: "TriggerOrder",
73
73
  label: "Missing Trigger Order",
74
- description: "Guarantee your flow execution order with the **Trigger Order** property introduced in Spring '22." + "value to their flows and guarantee their execution order. This priority value is not an " + "absolute value, so the values need not be sequentially numbered as 1, 2, 3, and so on.",
74
+ description: "Record-triggered Flows without a specified Trigger Order may execute in an unpredictable sequence. Setting a Trigger Order ensures your Flows run in the intended order.",
75
+ summary: "Trigger Order ensures predictable execution sequence",
75
76
  supportedTypes: [
76
77
  _internals.FlowType.autolaunchedType
77
78
  ],
@@ -62,7 +62,8 @@ let UnconnectedElement = class UnconnectedElement extends _RuleCommon.RuleCommon
62
62
  constructor(){
63
63
  super({
64
64
  ruleId: "unreachable-element",
65
- description: "Avoid unconnected elements that are not used by the flow to keep flows efficient and maintainable.",
65
+ description: "Unconnected elements never execute and add unnecessary clutter. Remove or connect unused Flow elements to keep Flows clean and efficient.",
66
+ summary: "Unconnected elements add clutter without executing",
66
67
  docRefs: [],
67
68
  label: "Unreachable Element",
68
69
  name: "UnconnectedElement",
@@ -70,7 +70,8 @@ let UnsafeRunningContext = class UnsafeRunningContext extends _RuleCommon.RuleCo
70
70
  ruleId: "unsafe-running-context",
71
71
  name: "UnsafeRunningContext",
72
72
  label: "Unsafe Running Context",
73
- description: `This flow is configured to run in System Mode without Sharing. This system context grants all running users the permission to view and edit all data in your org. Running a flow in System Mode without Sharing can lead to unsafe data access.`,
73
+ description: "Flows configured to run in System Mode without Sharing grant access to all data, bypassing user permissions. Avoid this setting to prevent security risks and protect sensitive data.",
74
+ summary: "System mode without sharing creates security risks",
74
75
  supportedTypes: [
75
76
  ..._internals.FlowType.backEndTypes,
76
77
  ..._internals.FlowType.visualTypes
@@ -82,7 +82,8 @@ let UnusedVariable = class UnusedVariable extends _RuleCommon.RuleCommon {
82
82
  ruleId: "unused-variable",
83
83
  name: "UnusedVariable",
84
84
  label: "Unused Variable",
85
- description: "To maintain efficiency and manageability, avoid including variables that are never referenced.",
85
+ description: "Unused variables are never referenced and add unnecessary clutter. Remove them to keep Flows efficient and easy to maintain.",
86
+ summary: "Unused variables add clutter and hurt maintainability",
86
87
  supportedTypes: [
87
88
  ..._internals.FlowType.backEndTypes,
88
89
  ..._internals.FlowType.visualTypes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flow-scanner/lightning-flow-scanner-core",
3
3
  "description": "A lightweight engine for Flow metadata in Node.js, and browser environments. Assess and enhance Salesforce Flow automations for best practices, security, governor limits, and performance issues.",
4
- "version": "6.14.0",
4
+ "version": "6.15.1",
5
5
  "main": "index.js",
6
6
  "exports": {
7
7
  ".": {