@flow-scanner/lightning-flow-scanner-core 6.13.1 → 6.14.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.
- package/README.md +190 -88
- package/main/interfaces/IRulesConfig.d.ts +1 -0
- package/main/libs/ScanFlows.js +5 -0
- package/main/rules/APIVersion.js +1 -1
- package/main/rules/ActionCallsInLoop.js +3 -3
- package/main/rules/AutoLayout.js +2 -2
- package/main/rules/CopyAPIName.js +1 -1
- package/main/rules/DMLStatementInLoop.js +1 -1
- package/main/rules/DuplicateDMLOperation.js +1 -1
- package/main/rules/FlowDescription.js +1 -1
- package/main/rules/FlowName.js +1 -1
- package/main/rules/GetRecordAllFields.js +2 -2
- package/main/rules/HardcodedId.js +1 -1
- package/main/rules/HardcodedUrl.js +1 -1
- package/main/rules/InactiveFlow.js +1 -1
- package/main/rules/MissingFaultPath.js +1 -1
- package/main/rules/MissingMetadataDescription.js +1 -1
- package/main/rules/MissingNullHandler.js +1 -1
- package/main/rules/MissingRecordTriggerFilter.js +4 -2
- package/main/rules/ProcessBuilder.js +2 -2
- package/main/rules/RecordIdAsString.js +2 -2
- package/main/rules/RecursiveAfterUpdate.js +1 -1
- package/main/rules/SOQLQueryInLoop.js +1 -1
- package/main/rules/SameRecordFieldUpdates.js +1 -1
- package/main/rules/TransformInsteadOfLoop.js +1 -1
- package/main/rules/TriggerOrder.js +2 -2
- package/main/rules/UnconnectedElement.js +1 -1
- package/main/rules/UnusedVariable.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
- [Configure Severity Levels](#configure-severity-levels)
|
|
37
37
|
- [Overwrite Expressions](#overwrite-expressions)
|
|
38
38
|
- [Define Exceptions](#define-exceptions)
|
|
39
|
+
- [Exclude Flows from Scanning](#exclude-flows-from-scanning)
|
|
39
40
|
- [Scan Modes](#scan-modes)
|
|
40
41
|
- **[Installation](#installation)**
|
|
41
42
|
- [Distributions](#distributions)
|
|
@@ -52,140 +53,196 @@
|
|
|
52
53
|
|
|
53
54
|
> Want to code a new rule? → See [How to Write a Rule](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/docs/write-a-rule.md)
|
|
54
55
|
|
|
56
|
+
<!-- START GENERATED_RULES -->
|
|
55
57
|
### Action Call In A Loop
|
|
56
|
-
|
|
57
|
-
|
|
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.
|
|
59
|
+
|
|
60
|
+
**Rule ID:** `action-call-in-loop`
|
|
61
|
+
**Class Name:** _[ActionCallsInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/ActionCallsInLoop.ts)_
|
|
58
62
|
**Severity:** 🔴 *Error*
|
|
59
63
|
|
|
60
|
-
###
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
**
|
|
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.
|
|
66
|
+
|
|
67
|
+
**Rule ID:** `dml-in-loop`
|
|
68
|
+
**Class Name:** _[DMLStatementInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/DMLStatementInLoop.ts)_
|
|
69
|
+
**Severity:** 🔴 *Error*
|
|
64
70
|
|
|
65
|
-
###
|
|
66
|
-
|
|
67
|
-
|
|
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.
|
|
73
|
+
|
|
74
|
+
**Rule ID:** `duplicate-dml`
|
|
75
|
+
**Class Name:** _[DuplicateDMLOperation](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/DuplicateDMLOperation.ts)_
|
|
68
76
|
**Severity:** 🟡 *Warning*
|
|
69
77
|
|
|
70
|
-
### Excessive Cyclomatic Complexity
|
|
71
|
-
|
|
72
|
-
|
|
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.
|
|
80
|
+
|
|
81
|
+
**Rule ID:** `excessive-cyclomatic-complexity`
|
|
82
|
+
**Class Name:** _[CyclomaticComplexity](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/CyclomaticComplexity.ts)_
|
|
73
83
|
**Severity:** 🔵 *Note*
|
|
74
84
|
|
|
75
|
-
###
|
|
76
|
-
|
|
77
|
-
|
|
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`.
|
|
87
|
+
|
|
88
|
+
**Rule ID:** `invalid-naming-convention`
|
|
89
|
+
**Class Name:** _[FlowName](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/FlowName.ts)_
|
|
78
90
|
**Severity:** 🔴 *Error*
|
|
79
91
|
|
|
80
|
-
###
|
|
81
|
-
|
|
82
|
-
|
|
92
|
+
### Get Record All Fields
|
|
93
|
+
Following the principle of least privilege (PoLP), avoid using **Get Records** with “Automatically store all fields” unless necessary.
|
|
94
|
+
|
|
95
|
+
**Rule ID:** `get-record-all-fields`
|
|
96
|
+
**Class Name:** _[GetRecordAllFields](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/GetRecordAllFields.ts)_
|
|
83
97
|
**Severity:** 🟡 *Warning*
|
|
84
98
|
|
|
85
|
-
###
|
|
86
|
-
|
|
87
|
-
|
|
99
|
+
### Hardcoded Id
|
|
100
|
+
Avoid hard-coding IDs because they are org specific. Instead, pass them into variables at the start of the flow—via merge-field URL parameters or a **Get Records** element.
|
|
101
|
+
|
|
102
|
+
**Rule ID:** `hardcoded-id`
|
|
103
|
+
**Class Name:** _[HardcodedId](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/HardcodedId.ts)_
|
|
88
104
|
**Severity:** 🔴 *Error*
|
|
89
105
|
|
|
90
|
-
###
|
|
91
|
-
|
|
92
|
-
**Rule ID:** `get-record-all-fields`
|
|
93
|
-
**Severity:** 🟡 *Warning*
|
|
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.
|
|
94
108
|
|
|
95
|
-
|
|
96
|
-
_[
|
|
97
|
-
**Rule ID:** `hardcoded-id`
|
|
109
|
+
**Rule ID:** `hardcoded-url`
|
|
110
|
+
**Class Name:** _[HardcodedUrl](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/HardcodedUrl.ts)_
|
|
98
111
|
**Severity:** 🔴 *Error*
|
|
99
112
|
|
|
100
|
-
###
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
**
|
|
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.
|
|
115
|
+
|
|
116
|
+
**Rule ID:** `inactive-flow`
|
|
117
|
+
**Class Name:** _[InactiveFlow](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/InactiveFlow.ts)_
|
|
118
|
+
**Severity:** 🟡 *Warning*
|
|
119
|
+
|
|
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.
|
|
104
122
|
|
|
105
|
-
|
|
106
|
-
_[
|
|
107
|
-
**Rule ID:** `inactive-flow`
|
|
123
|
+
**Rule ID:** `invalid-api-version`
|
|
124
|
+
**Class Name:** _[APIVersion](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/APIVersion.ts)_
|
|
108
125
|
**Severity:** 🟡 *Warning*
|
|
109
126
|
|
|
110
|
-
### Missing Auto Layout
|
|
111
|
-
|
|
112
|
-
|
|
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.
|
|
129
|
+
|
|
130
|
+
**Rule ID:** `missing-auto-layout`
|
|
131
|
+
**Class Name:** _[AutoLayout](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/AutoLayout.ts)_
|
|
113
132
|
**Severity:** 🔵 *Note*
|
|
114
133
|
|
|
115
|
-
### Missing Fault Path
|
|
116
|
-
|
|
117
|
-
|
|
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.
|
|
136
|
+
|
|
137
|
+
**Rule ID:** `missing-fault-path`
|
|
138
|
+
**Class Name:** _[MissingFaultPath](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingFaultPath.ts)_
|
|
118
139
|
**Severity:** 🟡 *Warning*
|
|
119
140
|
|
|
120
141
|
### Missing Filter Record Trigger 
|
|
121
|
-
|
|
122
|
-
|
|
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.
|
|
143
|
+
|
|
144
|
+
**Rule ID:** `missing-record-trigger-filter`
|
|
145
|
+
**Class Name:** _[MissingFilterRecordTrigger](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingFilterRecordTrigger.ts)_
|
|
123
146
|
**Severity:** 🟡 *Warning*
|
|
124
147
|
|
|
125
|
-
### Missing Flow Description
|
|
126
|
-
|
|
127
|
-
|
|
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.
|
|
150
|
+
|
|
151
|
+
**Rule ID:** `missing-flow-description`
|
|
152
|
+
**Class Name:** _[FlowDescription](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/FlowDescription.ts)_
|
|
128
153
|
**Severity:** 🔴 *Error*
|
|
129
154
|
|
|
130
155
|
### Missing Metadata Description 
|
|
131
|
-
|
|
132
|
-
|
|
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.
|
|
157
|
+
|
|
158
|
+
**Rule ID:** `missing-metadata-description`
|
|
159
|
+
**Class Name:** _[MissingMetadataDescription](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingMetadataDescription.ts)_
|
|
133
160
|
**Severity:** 🔴 *Error*
|
|
134
161
|
|
|
135
|
-
### Missing Null Handler
|
|
136
|
-
|
|
137
|
-
|
|
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.
|
|
164
|
+
|
|
165
|
+
**Rule ID:** `missing-null-handler`
|
|
166
|
+
**Class Name:** _[MissingNullHandler](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingNullHandler.ts)_
|
|
138
167
|
**Severity:** 🟡 *Warning*
|
|
139
168
|
|
|
140
|
-
### Missing Trigger Order
|
|
141
|
-
|
|
142
|
-
|
|
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.
|
|
171
|
+
|
|
172
|
+
**Rule ID:** `unspecified-trigger-order`
|
|
173
|
+
**Class Name:** _[TriggerOrder](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/TriggerOrder.ts)_
|
|
143
174
|
**Severity:** 🔵 *Note*
|
|
144
175
|
|
|
145
|
-
### Process Builder
|
|
146
|
-
|
|
147
|
-
|
|
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.
|
|
178
|
+
|
|
179
|
+
**Rule ID:** `process-builder-usage`
|
|
180
|
+
**Class Name:** _[ProcessBuilder](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/ProcessBuilder.ts)_
|
|
148
181
|
**Severity:** 🟡 *Warning*
|
|
149
182
|
|
|
150
183
|
### Record ID as String 
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
**
|
|
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.
|
|
185
|
+
|
|
186
|
+
**Rule ID:** `record-id-as-string`
|
|
187
|
+
**Class Name:** _[RecordIdAsString](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/RecordIdAsString.ts)_
|
|
188
|
+
**Severity:** 🔴 *Error*
|
|
154
189
|
|
|
155
|
-
### Recursive After Update
|
|
156
|
-
|
|
157
|
-
|
|
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.
|
|
192
|
+
|
|
193
|
+
**Rule ID:** `recursive-record-update`
|
|
194
|
+
**Class Name:** _[RecursiveAfterUpdate](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/RecursiveAfterUpdate.ts)_
|
|
158
195
|
**Severity:** 🟡 *Warning*
|
|
159
196
|
|
|
160
|
-
### Same Record Field Updates
|
|
161
|
-
|
|
162
|
-
|
|
197
|
+
### Same Record Field Updates
|
|
198
|
+
Similar to triggers, **before-save** contexts can update the same record via `$Record` without invoking DML.
|
|
199
|
+
|
|
200
|
+
**Rule ID:** `same-record-field-updates`
|
|
201
|
+
**Class Name:** _[SameRecordFieldUpdates](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/SameRecordFieldUpdates.ts)_
|
|
163
202
|
**Severity:** 🟡 *Warning*
|
|
164
203
|
|
|
165
|
-
### SOQL Query In A Loop
|
|
166
|
-
|
|
167
|
-
|
|
204
|
+
### SOQL Query In A Loop
|
|
205
|
+
To prevent exceeding Apex governor limits, consolidate all SOQL queries at the end of the flow.
|
|
206
|
+
|
|
207
|
+
**Rule ID:** `soql-in-loop`
|
|
208
|
+
**Class Name:** _[SOQLQueryInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/SOQLQueryInLoop.ts)_
|
|
168
209
|
**Severity:** 🔴 *Error*
|
|
169
210
|
|
|
170
211
|
### Transform Instead of Loop 
|
|
171
|
-
|
|
172
|
-
|
|
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.
|
|
213
|
+
|
|
214
|
+
**Rule ID:** `transform-instead-of-loop`
|
|
215
|
+
**Class Name:** _[TransformInsteadOfLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/TransformInsteadOfLoop.ts)_
|
|
173
216
|
**Severity:** 🔵 *Note*
|
|
174
217
|
|
|
175
|
-
###
|
|
176
|
-
|
|
177
|
-
|
|
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.
|
|
220
|
+
|
|
221
|
+
**Rule ID:** `unclear-api-naming`
|
|
222
|
+
**Class Name:** _[CopyAPIName](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/CopyAPIName.ts)_
|
|
178
223
|
**Severity:** 🟡 *Warning*
|
|
179
224
|
|
|
180
|
-
###
|
|
181
|
-
|
|
182
|
-
|
|
225
|
+
### Unreachable Element
|
|
226
|
+
Avoid unconnected elements that are not used by the flow to keep flows efficient and maintainable.
|
|
227
|
+
|
|
228
|
+
**Rule ID:** `unreachable-element`
|
|
229
|
+
**Class Name:** _[UnconnectedElement](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/UnconnectedElement.ts)_
|
|
230
|
+
**Severity:** 🟡 *Warning*
|
|
231
|
+
|
|
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.
|
|
234
|
+
|
|
235
|
+
**Rule ID:** `unsafe-running-context`
|
|
236
|
+
**Class Name:** _[UnsafeRunningContext](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/UnsafeRunningContext.ts)_
|
|
183
237
|
**Severity:** 🔴 *Error*
|
|
184
238
|
|
|
185
|
-
### Unused Variable
|
|
186
|
-
|
|
187
|
-
|
|
239
|
+
### Unused Variable
|
|
240
|
+
To maintain efficiency and manageability, avoid including variables that are never referenced.
|
|
241
|
+
|
|
242
|
+
**Rule ID:** `unused-variable`
|
|
243
|
+
**Class Name:** _[UnusedVariable](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/UnusedVariable.ts)_
|
|
188
244
|
**Severity:** 🟡 *Warning*
|
|
245
|
+
<!-- END GENERATED_RULES -->
|
|
189
246
|
|
|
190
247
|
---
|
|
191
248
|
|
|
@@ -196,6 +253,7 @@ It is recommend to configure and define:
|
|
|
196
253
|
- The severity of violating any specific rule.
|
|
197
254
|
- Expressions used for rules, such as REGEX patterns and comparison operators.
|
|
198
255
|
- Any known exceptions that should be ignored during scanning.
|
|
256
|
+
- Flows or directories to exclude from scanning entirely.
|
|
199
257
|
|
|
200
258
|
```json
|
|
201
259
|
{
|
|
@@ -290,6 +348,48 @@ _Example_
|
|
|
290
348
|
}
|
|
291
349
|
```
|
|
292
350
|
|
|
351
|
+
### Exclude Flows from Scanning
|
|
352
|
+
|
|
353
|
+
Lightning Flow Scanner provides two complementary ways to exclude flows from scanning:
|
|
354
|
+
|
|
355
|
+
#### Exclude by File Path (Node.js only)
|
|
356
|
+
|
|
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:
|
|
358
|
+
|
|
359
|
+
```json
|
|
360
|
+
{
|
|
361
|
+
"ignore": [
|
|
362
|
+
"**/testing/**",
|
|
363
|
+
"**/*_Deprecated.flow-meta.xml"
|
|
364
|
+
]
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
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
|
+
**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
|
+
|
|
372
|
+
#### Exclude by Flow API Name (Browser-compatible)
|
|
373
|
+
|
|
374
|
+
Exclude specific flows by their unique API names, regardless of their location. This is particularly useful for:
|
|
375
|
+
- Excluding specific flows without knowing their exact file path
|
|
376
|
+
- Working with metadata API deployments where directory structures may vary
|
|
377
|
+
- More precise control than path-based patterns
|
|
378
|
+
|
|
379
|
+
```json
|
|
380
|
+
{
|
|
381
|
+
"ignoreFlows": [
|
|
382
|
+
"My_Legacy_Flow",
|
|
383
|
+
"Temporary_Test_Flow",
|
|
384
|
+
"Deprecated_Process_Builder"
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
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
|
+
**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
|
+
|
|
293
393
|
### Scan Modes
|
|
294
394
|
|
|
295
395
|
#### Beta Mode
|
|
@@ -326,7 +426,7 @@ By default, Lightning Flow Scanner runs **all** default rules and merges any cus
|
|
|
326
426
|
**Privacy:** Zero user data collected. All processing is client-side. → See our [Security Policy](https://github.com/Flow-Scanner/lightning-flow-scanner?tab=security-ov-file).
|
|
327
427
|
|
|
328
428
|
### CICD Templates
|
|
329
|
-
Ready-to-use CI/CD templates and a **Copado Plugin**.
|
|
429
|
+
Ready-to-use CI/CD templates and a **Copado Plugin**.
|
|
330
430
|
|
|
331
431
|
| Platform | Type | Link |
|
|
332
432
|
|----------------|-----------------------------------|------|
|
|
@@ -369,6 +469,8 @@ Add a GitHub workflow file `.github/workflows/scan-flows.yml` to detect issues d
|
|
|
369
469
|
- name: Lightning Flow Scan
|
|
370
470
|
id: flowscanner
|
|
371
471
|
uses: Flow-Scanner/lightning-flow-scanner@main
|
|
472
|
+
with:
|
|
473
|
+
sarif-only: true # Strict mode for PRs
|
|
372
474
|
|
|
373
475
|
- name: Upload SARIF to Code Scanning
|
|
374
476
|
uses: github/codeql-action/upload-sarif@v3
|
|
@@ -388,12 +490,12 @@ parse("flows/*.xml").then(scan);
|
|
|
388
490
|
|
|
389
491
|
// Get SARIF output (e.g. for GitHub Code Scanning)
|
|
390
492
|
import { parse, scan, exportSarif } from "@flow-scanner/lightning-flow-scanner-core";
|
|
391
|
-
parse("flows/**/*.flow-meta.xml").then(scan).then(exportSarif)
|
|
493
|
+
parse("flows/**/*.flow-meta.xml").then(scan).then(exportSarif)
|
|
392
494
|
// .then(sarif => fs.writeFile("results.sarif", sarif))
|
|
393
495
|
|
|
394
496
|
// Generate Markdown documentation with Mermaid flow diagrams
|
|
395
497
|
import { parse, exportDiagram } from "@flow-scanner/lightning-flow-scanner-core";
|
|
396
|
-
parse("flows/**/*.flow-meta.xml").then(exportDiagram)
|
|
498
|
+
parse("flows/**/*.flow-meta.xml").then(exportDiagram)
|
|
397
499
|
// .then(md => fs.writeFile("flow-docs.md", md))
|
|
398
500
|
|
|
399
501
|
// Browser Usage (Tooling API)
|
|
@@ -413,13 +515,13 @@ For more on Programmatic API, types, and advanced usage of `@flow-scanner/lightn
|
|
|
413
515
|
|
|
414
516
|
> This project optionally uses [Volta](https://volta.sh) to guarantee the exact same Node.js and tool versions for every contributor.
|
|
415
517
|
>
|
|
416
|
-
> MacOs/Linux:
|
|
518
|
+
> MacOs/Linux:
|
|
417
519
|
> ```sh
|
|
418
520
|
> curl https://get.volta.sh | bash
|
|
419
521
|
> ```
|
|
420
522
|
> Windows:
|
|
421
523
|
> ```sh
|
|
422
|
-
> winget install Volta.Volta
|
|
524
|
+
> winget install Volta.Volta
|
|
423
525
|
> ```
|
|
424
526
|
> Volta will automatically install and lock the tool versions defined in `package.json`.
|
|
425
527
|
|
|
@@ -443,18 +545,18 @@ For more on Programmatic API, types, and advanced usage of `@flow-scanner/lightn
|
|
|
443
545
|
|
|
444
546
|
To compile just the core package::
|
|
445
547
|
```bash
|
|
446
|
-
pnpm build:core
|
|
548
|
+
pnpm build:core
|
|
447
549
|
```
|
|
448
550
|
|
|
449
551
|
4. Run tests:
|
|
450
552
|
|
|
451
553
|
```bash
|
|
452
|
-
pnpm test
|
|
554
|
+
pnpm test
|
|
453
555
|
```
|
|
454
556
|
|
|
455
557
|
Or to test a new version of the core:
|
|
456
558
|
```bash
|
|
457
|
-
pnpm test:core
|
|
559
|
+
pnpm test:core
|
|
458
560
|
```
|
|
459
561
|
|
|
460
562
|
5. Linking the core module locally(Optional):
|
package/main/libs/ScanFlows.js
CHANGED
|
@@ -37,8 +37,13 @@ function getSuppressionsForRule(rule, flowName, exceptions) {
|
|
|
37
37
|
}
|
|
38
38
|
function scan(parsedFlows, ruleOptions) {
|
|
39
39
|
const flows = [];
|
|
40
|
+
const ignoreFlows = (ruleOptions === null || ruleOptions === void 0 ? void 0 : ruleOptions.ignoreFlows) || [];
|
|
40
41
|
for (const flow of parsedFlows){
|
|
41
42
|
if (!flow.errorMessage && flow.flow) {
|
|
43
|
+
// Filter out flows whose names are in the ignore list
|
|
44
|
+
if (ignoreFlows.length > 0 && ignoreFlows.includes(flow.flow.name)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
42
47
|
flows.push(flow.flow);
|
|
43
48
|
}
|
|
44
49
|
}
|
package/main/rules/APIVersion.js
CHANGED
|
@@ -116,7 +116,7 @@ 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
|
|
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.",
|
|
120
120
|
supportedTypes: _internals.FlowType.allTypes(),
|
|
121
121
|
docRefs: []
|
|
122
122
|
});
|
|
@@ -20,14 +20,14 @@ 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,
|
|
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.",
|
|
24
24
|
docRefs: [
|
|
25
25
|
{
|
|
26
|
-
label: "
|
|
26
|
+
label: "Action Call In A Loop",
|
|
27
27
|
path: "https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_InvocableMethod.htm"
|
|
28
28
|
}
|
|
29
29
|
],
|
|
30
|
-
label: "Action Call In Loop",
|
|
30
|
+
label: "Action Call In A Loop",
|
|
31
31
|
name: "ActionCallsInLoop",
|
|
32
32
|
supportedTypes: _internals.FlowType.backEndTypes
|
|
33
33
|
}, {
|
package/main/rules/AutoLayout.js
CHANGED
|
@@ -67,8 +67,8 @@ let AutoLayout = class AutoLayout extends _RuleCommon.RuleCommon {
|
|
|
67
67
|
super({
|
|
68
68
|
ruleId: "missing-auto-layout",
|
|
69
69
|
name: "AutoLayout",
|
|
70
|
-
label: "Auto
|
|
71
|
-
description: "With Canvas Mode set to Auto-Layout,
|
|
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.",
|
|
72
72
|
supportedTypes: _internals.FlowType.allTypes(),
|
|
73
73
|
docRefs: []
|
|
74
74
|
}, {
|
|
@@ -62,7 +62,7 @@ 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
|
|
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.",
|
|
66
66
|
supportedTypes: _internals.FlowType.allTypes(),
|
|
67
67
|
docRefs: []
|
|
68
68
|
});
|
|
@@ -21,7 +21,7 @@ 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,
|
|
24
|
+
description: "To prevent exceeding Apex governor limits, consolidate all your database operations—record creation, updates, or deletions—at the conclusion of the flow.",
|
|
25
25
|
docRefs: [
|
|
26
26
|
{
|
|
27
27
|
label: "Flow Best Practices",
|
|
@@ -97,7 +97,7 @@ 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
|
|
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.",
|
|
101
101
|
supportedTypes: _internals.FlowType.visualTypes,
|
|
102
102
|
docRefs: []
|
|
103
103
|
});
|
|
@@ -64,7 +64,7 @@ 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.
|
|
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.",
|
|
68
68
|
docRefs: [],
|
|
69
69
|
label: "Missing Flow Description",
|
|
70
70
|
name: "FlowDescription",
|
package/main/rules/FlowName.js
CHANGED
|
@@ -67,7 +67,7 @@ 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
|
|
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`.",
|
|
71
71
|
docRefs: [
|
|
72
72
|
{
|
|
73
73
|
label: "Naming your Flows is more critical than ever. By Stephen Church",
|
|
@@ -69,10 +69,10 @@ 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
|
|
72
|
+
description: "Following the principle of least privilege (PoLP), avoid using **Get Records** with “Automatically store all fields” unless necessary.",
|
|
73
73
|
docRefs: [
|
|
74
74
|
{
|
|
75
|
-
label: "
|
|
75
|
+
label: "Get Records Stores All Fields",
|
|
76
76
|
path: "https://developer.salesforce.com/docs/atlas.en-us.salesforce_large_data_volumes_bp.meta/salesforce_large_data_volumes_bp/ldv_deployments_best_practices_soql_and_sosl.htm"
|
|
77
77
|
},
|
|
78
78
|
{
|
|
@@ -61,7 +61,7 @@ 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
|
|
64
|
+
description: "Avoid hard-coding IDs because they are org specific. Instead, pass them into variables at the start of the flow—via merge-field URL parameters or a **Get Records** element.",
|
|
65
65
|
supportedTypes: _internals.FlowType.allTypes(),
|
|
66
66
|
docRefs: [
|
|
67
67
|
{
|
|
@@ -19,7 +19,7 @@ let HardcodedUrl = class HardcodedUrl extends _RuleCommon.RuleCommon {
|
|
|
19
19
|
constructor(){
|
|
20
20
|
super({
|
|
21
21
|
ruleId: "hardcoded-url",
|
|
22
|
-
description: "Avoid hard-coding URLs
|
|
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.",
|
|
23
23
|
docRefs: [
|
|
24
24
|
{
|
|
25
25
|
label: "The Ultimate Guide to Salesforce Flow Best Practices",
|
|
@@ -65,7 +65,7 @@ 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
|
|
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.",
|
|
69
69
|
supportedTypes: _internals.FlowType.allTypes(),
|
|
70
70
|
docRefs: []
|
|
71
71
|
});
|
|
@@ -127,7 +127,7 @@ let MissingFaultPath = class MissingFaultPath extends _RuleCommon.RuleCommon {
|
|
|
127
127
|
constructor(){
|
|
128
128
|
super({
|
|
129
129
|
ruleId: "missing-fault-path",
|
|
130
|
-
description: "
|
|
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.",
|
|
131
131
|
docRefs: [
|
|
132
132
|
{
|
|
133
133
|
label: "Flow Best Practices",
|
|
@@ -66,7 +66,7 @@ let MissingMetadataDescription = class MissingMetadataDescription extends _RuleC
|
|
|
66
66
|
constructor(){
|
|
67
67
|
super({
|
|
68
68
|
ruleId: "missing-metadata-description",
|
|
69
|
-
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.",
|
|
70
70
|
docRefs: [],
|
|
71
71
|
label: "Missing Metadata Description",
|
|
72
72
|
name: "MissingMetadataDescription",
|
|
@@ -138,7 +138,7 @@ 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
|
|
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.",
|
|
142
142
|
docRefs: [],
|
|
143
143
|
label: "Missing Null Handler",
|
|
144
144
|
name: "MissingNullHandler",
|
|
@@ -79,12 +79,14 @@ let MissingRecordTriggerFilter = class MissingRecordTriggerFilter extends _RuleC
|
|
|
79
79
|
super({
|
|
80
80
|
ruleId: "missing-record-trigger-filter",
|
|
81
81
|
name: "MissingRecordTriggerFilter",
|
|
82
|
-
label: "Missing Record Trigger
|
|
83
|
-
description: "
|
|
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.",
|
|
84
84
|
supportedTypes: [
|
|
85
85
|
_internals.FlowType.autolaunchedType
|
|
86
86
|
],
|
|
87
87
|
docRefs: []
|
|
88
|
+
}, {
|
|
89
|
+
severity: "warning"
|
|
88
90
|
});
|
|
89
91
|
}
|
|
90
92
|
};
|
|
@@ -61,8 +61,8 @@ let ProcessBuilder = class ProcessBuilder extends _RuleCommon.RuleCommon {
|
|
|
61
61
|
super({
|
|
62
62
|
ruleId: "process-builder-usage",
|
|
63
63
|
name: "ProcessBuilder",
|
|
64
|
-
label: "Process Builder
|
|
65
|
-
description: "Salesforce is transitioning away from Workflow Rules and Process Builder in favor of Flow.
|
|
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.",
|
|
66
66
|
supportedTypes: _internals.FlowType.processBuilder,
|
|
67
67
|
docRefs: [
|
|
68
68
|
{
|
|
@@ -75,8 +75,8 @@ let RecordIdAsString = class RecordIdAsString extends _RuleCommon.RuleCommon {
|
|
|
75
75
|
super({
|
|
76
76
|
ruleId: "record-id-as-string",
|
|
77
77
|
name: "RecordIdAsString",
|
|
78
|
-
label: "Record ID as String
|
|
79
|
-
description: "Detects flows using a String variable named
|
|
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.",
|
|
80
80
|
supportedTypes: [
|
|
81
81
|
..._internals.FlowType.visualTypes,
|
|
82
82
|
_internals.FlowType.autolaunchedType
|
|
@@ -105,7 +105,7 @@ let RecursiveAfterUpdate = class RecursiveAfterUpdate extends _RuleCommon.RuleCo
|
|
|
105
105
|
constructor(){
|
|
106
106
|
super({
|
|
107
107
|
ruleId: "recursive-record-update",
|
|
108
|
-
description: "After
|
|
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.",
|
|
109
109
|
docRefs: [
|
|
110
110
|
{
|
|
111
111
|
label: "Learn about same record field updates",
|
|
@@ -19,7 +19,7 @@ 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,
|
|
22
|
+
description: "To prevent exceeding Apex governor limits, consolidate all SOQL queries at the end of the flow.",
|
|
23
23
|
docRefs: [
|
|
24
24
|
{
|
|
25
25
|
label: "Flow Best Practices",
|
|
@@ -89,7 +89,7 @@ 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: "
|
|
92
|
+
description: "Similar to triggers, **before-save** contexts can update the same record via `$Record` without invoking DML.",
|
|
93
93
|
supportedTypes: [
|
|
94
94
|
..._internals.FlowType.backEndTypes
|
|
95
95
|
],
|
|
@@ -79,7 +79,7 @@ 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.
|
|
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.",
|
|
83
83
|
supportedTypes: _internals.FlowType.allTypes(),
|
|
84
84
|
docRefs: [
|
|
85
85
|
{
|
|
@@ -70,8 +70,8 @@ let TriggerOrder = class TriggerOrder extends _RuleCommon.RuleCommon {
|
|
|
70
70
|
super({
|
|
71
71
|
ruleId: "unspecified-trigger-order",
|
|
72
72
|
name: "TriggerOrder",
|
|
73
|
-
label: "Trigger Order",
|
|
74
|
-
description: "
|
|
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.",
|
|
75
75
|
supportedTypes: [
|
|
76
76
|
_internals.FlowType.autolaunchedType
|
|
77
77
|
],
|
|
@@ -62,7 +62,7 @@ let UnconnectedElement = class UnconnectedElement extends _RuleCommon.RuleCommon
|
|
|
62
62
|
constructor(){
|
|
63
63
|
super({
|
|
64
64
|
ruleId: "unreachable-element",
|
|
65
|
-
description: "
|
|
65
|
+
description: "Avoid unconnected elements that are not used by the flow to keep flows efficient and maintainable.",
|
|
66
66
|
docRefs: [],
|
|
67
67
|
label: "Unreachable Element",
|
|
68
68
|
name: "UnconnectedElement",
|
|
@@ -82,7 +82,7 @@ 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
|
|
85
|
+
description: "To maintain efficiency and manageability, avoid including variables that are never referenced.",
|
|
86
86
|
supportedTypes: [
|
|
87
87
|
..._internals.FlowType.backEndTypes,
|
|
88
88
|
..._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.
|
|
4
|
+
"version": "6.14.0",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|