@flow-scanner/lightning-flow-scanner-core 6.11.4 → 6.11.5
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 +40 -13
- package/main/interfaces/IRuleConfig.d.ts +2 -1
- package/main/interfaces/IRuleDefinition.d.ts +1 -0
- package/main/libs/GetRuleDefinitions.js +26 -63
- package/main/libs/ScanFlows.js +17 -9
- package/main/models/RuleCommon.d.ts +1 -1
- package/main/models/RuleCommon.js +5 -2
- package/main/models/RuleInfo.d.ts +6 -6
- package/main/models/RuleInfo.js +5 -5
- package/main/rules/APIVersion.js +2 -1
- package/main/rules/ActionCallsInLoop.js +1 -0
- package/main/rules/AutoLayout.js +1 -0
- package/main/rules/CopyAPIName.js +2 -1
- package/main/rules/CyclomaticComplexity.js +2 -1
- package/main/rules/DMLStatementInLoop.js +1 -0
- package/main/rules/DuplicateDMLOperation.js +1 -0
- package/main/rules/FlowDescription.js +1 -0
- package/main/rules/FlowName.js +1 -0
- package/main/rules/GetRecordAllFields.js +1 -0
- package/main/rules/HardcodedId.js +1 -0
- package/main/rules/HardcodedUrl.js +1 -0
- package/main/rules/InactiveFlow.js +1 -0
- package/main/rules/MissingFaultPath.js +1 -0
- package/main/rules/MissingMetadataDescription.js +1 -0
- package/main/rules/MissingNullHandler.js +1 -0
- package/main/rules/{MissingFilterRecordTrigger.d.ts → MissingRecordTriggerFilter.d.ts} +1 -1
- package/main/rules/{MissingFilterRecordTrigger.js → MissingRecordTriggerFilter.js} +5 -4
- package/main/rules/ProcessBuilder.js +2 -1
- package/main/rules/RecordIdAsString.js +1 -0
- package/main/rules/RecursiveAfterUpdate.js +1 -0
- package/main/rules/SOQLQueryInLoop.js +1 -0
- package/main/rules/SameRecordFieldUpdates.js +1 -0
- package/main/rules/TransformInsteadOfLoop.js +1 -0
- package/main/rules/TriggerOrder.js +1 -0
- package/main/rules/UnconnectedElement.js +2 -1
- package/main/rules/UnsafeRunningContext.js +1 -0
- package/main/rules/UnusedVariable.js +1 -0
- package/main/store/RuleRegistry.d.ts +22 -0
- package/main/store/RuleRegistry.js +205 -0
- package/package.json +1 -1
- package/main/libs/DynamicRule.d.ts +0 -4
- package/main/libs/DynamicRule.js +0 -19
- package/main/store/DefaultRuleStore.d.ts +0 -2
- package/main/store/DefaultRuleStore.js +0 -76
package/README.md
CHANGED
|
@@ -54,110 +54,137 @@
|
|
|
54
54
|
|
|
55
55
|
### Action Calls In Loop
|
|
56
56
|
_[ActionCallsInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/ActionCallsInLoop.ts)_ – 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.
|
|
57
|
+
**Rule ID:** `action-call-in-loop`
|
|
57
58
|
**Severity:** 🔴 *Error*
|
|
58
59
|
|
|
59
|
-
###
|
|
60
|
+
### Invalid API Version
|
|
60
61
|
_[APIVersion](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/APIVersion.ts)_ – 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.
|
|
62
|
+
**Rule ID:** `invalid-api-version`
|
|
61
63
|
**Severity:** 🟡 *Warning*
|
|
62
64
|
|
|
63
|
-
###
|
|
64
|
-
_[AutoLayout](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/AutoLayout.ts)_ – With Canvas Mode set to Auto-Layout, elements are spaced, connected, and aligned automatically, keeping your Flow neatly organized—saving you time.
|
|
65
|
-
**Severity:** 🔵 *Note*
|
|
66
|
-
|
|
67
|
-
### Copy API Name
|
|
65
|
+
### Unclear API Name
|
|
68
66
|
_[CopyAPIName](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/CopyAPIName.ts)_ – 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.
|
|
67
|
+
**Rule ID:** `unclear-api-naming`
|
|
69
68
|
**Severity:** 🟡 *Warning*
|
|
70
69
|
|
|
71
|
-
### Cyclomatic Complexity
|
|
70
|
+
### Excessive Cyclomatic Complexity
|
|
72
71
|
_[CyclomaticComplexity](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/CyclomaticComplexity.ts)_ – 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 cyclomatic complexity within a single flow, ensuring maintainability and simplicity.
|
|
72
|
+
**Rule ID:** `excessive-cyclomatic-complexity`
|
|
73
73
|
**Severity:** 🔵 *Note*
|
|
74
74
|
|
|
75
75
|
### DML Statement In A Loop
|
|
76
76
|
_[DMLStatementInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/DMLStatementInLoop.ts)_ – To prevent exceeding Apex governor limits, consolidate all your database operations—record creation, updates, or deletions—at the conclusion of the flow.
|
|
77
|
+
**Rule ID:** `dml-in-loop`
|
|
77
78
|
**Severity:** 🔴 *Error*
|
|
78
79
|
|
|
79
80
|
### Duplicate DML Operation
|
|
80
81
|
_[DuplicateDMLOperation](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/DuplicateDMLOperation.ts)_ – 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.
|
|
82
|
+
**Rule ID:** `duplicate-dml`
|
|
81
83
|
**Severity:** 🟡 *Warning*
|
|
82
84
|
|
|
83
85
|
### Flow Naming Convention
|
|
84
86
|
_[FlowName](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/FlowName.ts)_ – 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
|
+
**Rule ID:** `invalid-naming-convention`
|
|
85
88
|
**Severity:** 🔴 *Error*
|
|
86
89
|
|
|
87
|
-
### Get
|
|
90
|
+
### Get Records Stores All Fields
|
|
88
91
|
_[GetRecordAllFields](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/GetRecordAllFields.ts)_ – Following the principle of least privilege (PoLP), avoid using **Get Records** with “Automatically store all fields” unless necessary.
|
|
92
|
+
**Rule ID:** `get-record-all-fields`
|
|
89
93
|
**Severity:** 🟡 *Warning*
|
|
90
94
|
|
|
91
95
|
### Hardcoded Id
|
|
92
96
|
_[HardcodedId](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/HardcodedId.ts)_ – 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.
|
|
97
|
+
**Rule ID:** `hardcoded-id`
|
|
93
98
|
**Severity:** 🔴 *Error*
|
|
94
99
|
|
|
95
100
|
### Hardcoded Url
|
|
96
101
|
_[HardcodedUrl](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/HardcodedUrl.ts)_ – Avoid hard-coding URLs because they are environment specific. Use an `$API` formula (preferred) or environment-specific sources like custom labels, metadata, or settings.
|
|
102
|
+
**Rule ID:** `hardcoded-url`
|
|
97
103
|
**Severity:** 🔴 *Error*
|
|
98
104
|
|
|
99
105
|
### Inactive Flow
|
|
100
106
|
_[InactiveFlow](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/InactiveFlow.ts)_ – 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.
|
|
107
|
+
**Rule ID:** `inactive-flow`
|
|
101
108
|
**Severity:** 🟡 *Warning*
|
|
102
109
|
|
|
110
|
+
### Missing Auto Layout
|
|
111
|
+
_[AutoLayout](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/AutoLayout.ts)_ – With Canvas Mode set to Auto-Layout, elements are spaced, connected, and aligned automatically, keeping your Flow neatly organized—saving you time.
|
|
112
|
+
**Rule ID:** `missing-auto-layout`
|
|
113
|
+
**Severity:** 🔵 *Note*
|
|
114
|
+
|
|
103
115
|
### Missing Fault Path
|
|
104
116
|
_[MissingFaultPath](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingFaultPath.ts)_ – 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.
|
|
117
|
+
**Rule ID:** `missing-fault-path`
|
|
105
118
|
**Severity:** 🟡 *Warning*
|
|
106
119
|
|
|
107
120
|
### Missing Filter Record Trigger 
|
|
108
121
|
_[MissingFilterRecordTrigger](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingFilterRecordTrigger.ts)_ – 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.
|
|
122
|
+
**Rule ID:** `missing-record-trigger-filter`
|
|
109
123
|
**Severity:** 🟡 *Warning*
|
|
110
124
|
|
|
111
125
|
### Missing Flow Description
|
|
112
126
|
_[FlowDescription](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/FlowDescription.ts)_ – Descriptions play a vital role in documentation. It is highly recommended to include details about where a flow is used and its intended purpose.
|
|
127
|
+
**Rule ID:** `missing-flow-description`
|
|
113
128
|
**Severity:** 🔴 *Error*
|
|
114
129
|
|
|
115
130
|
### Missing Metadata Description 
|
|
116
131
|
_[MissingMetadataDescription](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingMetadataDescription.ts)_ – 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.
|
|
132
|
+
**Rule ID:** `missing-metadata-description`
|
|
117
133
|
**Severity:** 🔴 *Error*
|
|
118
134
|
|
|
119
135
|
### Missing Null Handler
|
|
120
136
|
_[MissingNullHandler](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/MissingNullHandler.ts)_ – 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.
|
|
137
|
+
**Rule ID:** `missing-null-handler`
|
|
121
138
|
**Severity:** 🟡 *Warning*
|
|
122
139
|
|
|
140
|
+
### Missing Trigger Order
|
|
141
|
+
_[TriggerOrder](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/TriggerOrder.ts)_ – Guarantee your flow execution order with the **Trigger Order** property introduced in Spring ’22.
|
|
142
|
+
**Rule ID:** `missing-trigger-order`
|
|
143
|
+
**Severity:** 🔵 *Note*
|
|
144
|
+
|
|
123
145
|
### Process Builder
|
|
124
146
|
_[ProcessBuilder](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/ProcessBuilder.ts)_ – Salesforce is transitioning away from Workflow Rules and Process Builder in favor of Flow. Begin migrating your organization’s automation to Flow.
|
|
147
|
+
**Rule ID:** `process-builder-usage`
|
|
125
148
|
**Severity:** 🟡 *Warning*
|
|
126
149
|
|
|
127
150
|
### Record ID as String 
|
|
128
151
|
_[RecordIdAsString](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/RecordIdAsString.ts)_ – 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.
|
|
152
|
+
**Rule ID:** `record-id-as-string`
|
|
129
153
|
**Severity:** 🔵 *Note*
|
|
130
154
|
|
|
131
155
|
### Recursive After Update
|
|
132
156
|
_[RecursiveAfterUpdate](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/RecursiveAfterUpdate.ts)_ – 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.
|
|
157
|
+
**Rule ID:** `recursive-record-update`
|
|
133
158
|
**Severity:** 🟡 *Warning*
|
|
134
159
|
|
|
135
160
|
### Same Record Field Updates
|
|
136
161
|
_[SameRecordFieldUpdates](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/SameRecordFieldUpdates.ts)_ – Similar to triggers, **before-save** contexts can update the same record via `$Record` without invoking DML.
|
|
162
|
+
**Rule ID:** `same-record-field-updates`
|
|
137
163
|
**Severity:** 🟡 *Warning*
|
|
138
164
|
|
|
139
165
|
### SOQL Query In A Loop
|
|
140
166
|
_[SOQLQueryInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/SOQLQueryInLoop.ts)_ – To prevent exceeding Apex governor limits, consolidate all SOQL queries at the end of the flow.
|
|
167
|
+
**Rule ID:** `soql-in-loop`
|
|
141
168
|
**Severity:** 🔴 *Error*
|
|
142
169
|
|
|
143
170
|
### Transform Instead of Loop 
|
|
144
171
|
_[TransformInsteadOfLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/TransformInsteadOfLoop.ts)_ – 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.
|
|
172
|
+
**Rule ID:** `transform-instead-of-loop`
|
|
145
173
|
**Severity:** 🔵 *Note*
|
|
146
174
|
|
|
147
|
-
###
|
|
148
|
-
_[TriggerOrder](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/TriggerOrder.ts)_ – Guarantee your flow execution order with the **Trigger Order** property introduced in Spring ’22.
|
|
149
|
-
**Severity:** 🔵 *Note*
|
|
150
|
-
|
|
151
|
-
### Unconnected Element
|
|
175
|
+
### Unreachable Element
|
|
152
176
|
_[UnconnectedElement](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/UnconnectedElement.ts)_ – Avoid unconnected elements that are not used by the flow to keep flows efficient and maintainable.
|
|
177
|
+
**Rule ID:** `unreachable-element`
|
|
153
178
|
**Severity:** 🟡 *Warning*
|
|
154
179
|
|
|
155
180
|
### Unsafe Running Context
|
|
156
181
|
_[UnsafeRunningContext](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/UnsafeRunningContext.ts)_ – This flow is configured to run in **System Mode without Sharing**, granting all users permission to view and edit all data. This can lead to unsafe data access.
|
|
182
|
+
**Rule ID:** `unsafe-running-context`
|
|
157
183
|
**Severity:** 🔴 *Error*
|
|
158
184
|
|
|
159
185
|
### Unused Variable
|
|
160
186
|
_[UnusedVariable](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/UnusedVariable.ts)_ – To maintain efficiency and manageability, avoid including variables that are never referenced.
|
|
187
|
+
**Rule ID:** `unused-variable`
|
|
161
188
|
**Severity:** 🟡 *Warning*
|
|
162
189
|
|
|
163
190
|
---
|
|
@@ -16,80 +16,43 @@ _export(exports, {
|
|
|
16
16
|
return getRules;
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
|
-
const
|
|
20
|
-
const _DynamicRule = require("./DynamicRule");
|
|
19
|
+
const _RuleRegistry = require("../store/RuleRegistry");
|
|
21
20
|
function GetRuleDefinitions(ruleConfig, options) {
|
|
22
|
-
const selectedRules = [];
|
|
23
21
|
const includeBeta = (options === null || options === void 0 ? void 0 : options.betaMode) === true || (options === null || options === void 0 ? void 0 : options.betamode) === true;
|
|
24
|
-
// Default to "merged" mode for backward compatibility
|
|
25
22
|
const rulesMode = (options === null || options === void 0 ? void 0 : options.ruleMode) || "merged";
|
|
26
|
-
|
|
23
|
+
const selectedRules = [];
|
|
24
|
+
const ruleIds = _RuleRegistry.ruleRegistry.getAllRuleIds(includeBeta);
|
|
25
|
+
// ISOLATED MODE
|
|
27
26
|
if (rulesMode === "isolated" && ruleConfig && ruleConfig.size > 0) {
|
|
28
|
-
for (const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// Apply custom severity if provided
|
|
38
|
-
const configuredSeverity = customConfig === null || customConfig === void 0 ? void 0 : customConfig["severity"];
|
|
39
|
-
if (configuredSeverity && (configuredSeverity === "error" || configuredSeverity === "warning" || configuredSeverity === "note")) {
|
|
40
|
-
matchedRule.severity = configuredSeverity;
|
|
41
|
-
}
|
|
42
|
-
selectedRules.push(matchedRule);
|
|
43
|
-
} catch (error) {
|
|
44
|
-
console.log(error.message);
|
|
27
|
+
for (const key of ruleConfig.keys()){
|
|
28
|
+
// key can now be either ruleId (new) or legacyName (old config compatibility)
|
|
29
|
+
const entry = _RuleRegistry.ruleRegistry.get(key);
|
|
30
|
+
if (!entry) continue;
|
|
31
|
+
const config = ruleConfig.get(key);
|
|
32
|
+
if ((config === null || config === void 0 ? void 0 : config.enabled) === false) continue;
|
|
33
|
+
const rule = _RuleRegistry.ruleRegistry.createInstance(entry.ruleId); // Always use ruleId to instantiate
|
|
34
|
+
if (config === null || config === void 0 ? void 0 : config.severity) {
|
|
35
|
+
rule.severity = config.severity;
|
|
45
36
|
}
|
|
37
|
+
selectedRules.push(rule);
|
|
46
38
|
}
|
|
47
39
|
return selectedRules;
|
|
48
40
|
}
|
|
49
|
-
//
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
// Process each rule
|
|
62
|
-
for (const ruleName of allRuleNames){
|
|
63
|
-
try {
|
|
64
|
-
// Check if there's a custom config for this rule
|
|
65
|
-
const customConfig = ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.get(ruleName);
|
|
66
|
-
// Skip if explicitly disabled
|
|
67
|
-
if (customConfig && customConfig["enabled"] === false) {
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
// Create the rule instance
|
|
71
|
-
const matchedRule = new _DynamicRule.DynamicRule(ruleName, includeBeta);
|
|
72
|
-
// Apply custom severity if provided
|
|
73
|
-
const configuredSeverity = customConfig === null || customConfig === void 0 ? void 0 : customConfig["severity"];
|
|
74
|
-
if (configuredSeverity && (configuredSeverity === "error" || configuredSeverity === "warning" || configuredSeverity === "note")) {
|
|
75
|
-
matchedRule.severity = configuredSeverity;
|
|
76
|
-
}
|
|
77
|
-
selectedRules.push(matchedRule);
|
|
78
|
-
} catch (error) {
|
|
79
|
-
console.log(error.message);
|
|
41
|
+
// MERGED MODE (default)
|
|
42
|
+
for (const ruleId of ruleIds){
|
|
43
|
+
const rule = _RuleRegistry.ruleRegistry.createInstance(ruleId);
|
|
44
|
+
var _ruleConfig_get;
|
|
45
|
+
// Try to find config by ruleId first, then fall back to legacy name
|
|
46
|
+
const config = (_ruleConfig_get = ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.get(rule.ruleId)) !== null && _ruleConfig_get !== void 0 ? _ruleConfig_get : ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.get(rule.name) // rule.name is the legacy camelCase name (e.g. "ActionCallsInLoop")
|
|
47
|
+
;
|
|
48
|
+
if ((config === null || config === void 0 ? void 0 : config.enabled) === false) continue;
|
|
49
|
+
if (config === null || config === void 0 ? void 0 : config.severity) {
|
|
50
|
+
rule.severity = config.severity;
|
|
80
51
|
}
|
|
52
|
+
selectedRules.push(rule);
|
|
81
53
|
}
|
|
82
54
|
return selectedRules;
|
|
83
55
|
}
|
|
84
56
|
function getRules(ruleNames, options) {
|
|
85
|
-
|
|
86
|
-
const ruleSeverityMap = new Map(ruleNames.map((name)=>[
|
|
87
|
-
name,
|
|
88
|
-
{
|
|
89
|
-
severity: "error"
|
|
90
|
-
}
|
|
91
|
-
]));
|
|
92
|
-
return GetRuleDefinitions(ruleSeverityMap, options);
|
|
93
|
-
}
|
|
94
|
-
return GetRuleDefinitions(undefined, options);
|
|
57
|
+
return _RuleRegistry.ruleRegistry.getRulesByNames(ruleNames, options);
|
|
95
58
|
}
|
package/main/libs/ScanFlows.js
CHANGED
|
@@ -20,6 +20,21 @@ 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
|
+
function getRuleConfigByIdOrName(rule, rulesConfig) {
|
|
24
|
+
if (!rulesConfig) return undefined;
|
|
25
|
+
// Try ruleId first, then fall back to name
|
|
26
|
+
return rulesConfig[rule.ruleId] || rulesConfig[rule.name];
|
|
27
|
+
}
|
|
28
|
+
function getSuppressionsForRule(rule, flowName, exceptions) {
|
|
29
|
+
if (!(exceptions === null || exceptions === void 0 ? void 0 : exceptions[flowName])) return [];
|
|
30
|
+
const flowExceptions = exceptions[flowName];
|
|
31
|
+
// Try ruleId first, then fall back to name
|
|
32
|
+
const rawSuppressions = flowExceptions[rule.ruleId] || flowExceptions[rule.name];
|
|
33
|
+
// If wildcard exists, return only wildcard; otherwise return array or empty
|
|
34
|
+
return (rawSuppressions === null || rawSuppressions === void 0 ? void 0 : rawSuppressions.includes("*")) ? [
|
|
35
|
+
"*"
|
|
36
|
+
] : rawSuppressions !== null && rawSuppressions !== void 0 ? rawSuppressions : [];
|
|
37
|
+
}
|
|
23
38
|
function scan(parsedFlows, ruleOptions) {
|
|
24
39
|
const flows = [];
|
|
25
40
|
for (const flow of parsedFlows){
|
|
@@ -49,19 +64,12 @@ function ScanFlows(flows, ruleOptions) {
|
|
|
49
64
|
const ruleResults = [];
|
|
50
65
|
for (const rule of selectedRules){
|
|
51
66
|
try {
|
|
52
|
-
var _ruleOptions_rules, _ruleOptions_exceptions_flow_name, _ruleOptions_exceptions;
|
|
53
67
|
if (!rule.supportedTypes.includes(flow.type)) {
|
|
54
68
|
ruleResults.push(new _internals.RuleResult(rule, []));
|
|
55
69
|
continue;
|
|
56
70
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
config = ruleOptions.rules[rule.name];
|
|
60
|
-
}
|
|
61
|
-
const rawSuppressions = ruleOptions === null || ruleOptions === void 0 ? void 0 : (_ruleOptions_exceptions = ruleOptions.exceptions) === null || _ruleOptions_exceptions === void 0 ? void 0 : (_ruleOptions_exceptions_flow_name = _ruleOptions_exceptions[flow.name]) === null || _ruleOptions_exceptions_flow_name === void 0 ? void 0 : _ruleOptions_exceptions_flow_name[rule.name];
|
|
62
|
-
const suppressions = (rawSuppressions === null || rawSuppressions === void 0 ? void 0 : rawSuppressions.includes("*")) ? [
|
|
63
|
-
"*"
|
|
64
|
-
] : rawSuppressions !== null && rawSuppressions !== void 0 ? rawSuppressions : [];
|
|
71
|
+
const config = getRuleConfigByIdOrName(rule, ruleOptions === null || ruleOptions === void 0 ? void 0 : ruleOptions.rules);
|
|
72
|
+
const suppressions = getSuppressionsForRule(rule, flow.name, ruleOptions === null || ruleOptions === void 0 ? void 0 : ruleOptions.exceptions);
|
|
65
73
|
const result = config && Object.keys(config).length > 0 ? rule.execute(flow, config, suppressions) : rule.execute(flow, undefined, suppressions);
|
|
66
74
|
if (result.severity !== rule.severity) {
|
|
67
75
|
result.severity = rule.severity;
|
|
@@ -11,8 +11,8 @@ export declare abstract class RuleCommon {
|
|
|
11
11
|
name: string;
|
|
12
12
|
severity?: string;
|
|
13
13
|
supportedTypes: string[];
|
|
14
|
-
suppressionElement?: string;
|
|
15
14
|
uri?: string;
|
|
15
|
+
readonly ruleId: string;
|
|
16
16
|
constructor(info: RuleInfo, optional?: {
|
|
17
17
|
severity?: string;
|
|
18
18
|
});
|
|
@@ -69,6 +69,9 @@ let RuleCommon = class RuleCommon {
|
|
|
69
69
|
if (suppressions.includes("*")) {
|
|
70
70
|
return new _internals.RuleResult(this, []);
|
|
71
71
|
}
|
|
72
|
+
if (suppressions.includes(this.ruleId) || suppressions.includes(this.name)) {
|
|
73
|
+
return new _internals.RuleResult(this, []);
|
|
74
|
+
}
|
|
72
75
|
const suppSet = new Set(suppressions);
|
|
73
76
|
let violations = this.check(flow, options, suppSet);
|
|
74
77
|
violations = violations.filter((v)=>!suppSet.has(v.name));
|
|
@@ -131,8 +134,9 @@ let RuleCommon = class RuleCommon {
|
|
|
131
134
|
_define_property(this, "name", void 0);
|
|
132
135
|
_define_property(this, "severity", void 0);
|
|
133
136
|
_define_property(this, "supportedTypes", void 0);
|
|
134
|
-
_define_property(this, "suppressionElement", void 0);
|
|
135
137
|
_define_property(this, "uri", void 0);
|
|
138
|
+
_define_property(this, "ruleId", void 0);
|
|
139
|
+
this.ruleId = info.ruleId;
|
|
136
140
|
this.name = info.name;
|
|
137
141
|
this.supportedTypes = info.supportedTypes;
|
|
138
142
|
this.label = info.label;
|
|
@@ -148,6 +152,5 @@ let RuleCommon = class RuleCommon {
|
|
|
148
152
|
}
|
|
149
153
|
var _optional_severity;
|
|
150
154
|
this.severity = (_optional_severity = optional === null || optional === void 0 ? void 0 : optional.severity) !== null && _optional_severity !== void 0 ? _optional_severity : "error";
|
|
151
|
-
this.suppressionElement = info.suppressionElement;
|
|
152
155
|
}
|
|
153
156
|
};
|
|
@@ -24,7 +24,12 @@ export declare class RuleInfo {
|
|
|
24
24
|
*/
|
|
25
25
|
label: string;
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* Stable public identifier used for config, suppression, and reporting.
|
|
28
|
+
*/
|
|
29
|
+
ruleId: string;
|
|
30
|
+
/**
|
|
31
|
+
* Legacy rule name (class-based identifier).
|
|
32
|
+
* Kept for backward compatibility.
|
|
28
33
|
*/
|
|
29
34
|
name: string;
|
|
30
35
|
/**
|
|
@@ -32,9 +37,4 @@ export declare class RuleInfo {
|
|
|
32
37
|
* Use defined types in @see FlowType
|
|
33
38
|
*/
|
|
34
39
|
supportedTypes: string[];
|
|
35
|
-
/**
|
|
36
|
-
* (Optional) The element that can be used to suppress this rule.
|
|
37
|
-
* @see AdvancedSuppression
|
|
38
|
-
*/
|
|
39
|
-
suppressionElement?: string;
|
|
40
40
|
}
|
package/main/models/RuleInfo.js
CHANGED
|
@@ -34,15 +34,15 @@ let RuleInfo = class RuleInfo {
|
|
|
34
34
|
* This property is being displayed on sf cli and on vsce
|
|
35
35
|
*/ _define_property(this, "label", void 0);
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* Stable public identifier used for config, suppression, and reporting.
|
|
38
|
+
*/ _define_property(this, "ruleId", void 0);
|
|
39
|
+
/**
|
|
40
|
+
* Legacy rule name (class-based identifier).
|
|
41
|
+
* Kept for backward compatibility.
|
|
38
42
|
*/ _define_property(this, "name", void 0);
|
|
39
43
|
/**
|
|
40
44
|
* The types supported by this rule (e.g., Flow, Process).
|
|
41
45
|
* Use defined types in @see FlowType
|
|
42
46
|
*/ _define_property(this, "supportedTypes", void 0);
|
|
43
|
-
/**
|
|
44
|
-
* (Optional) The element that can be used to suppress this rule.
|
|
45
|
-
* @see AdvancedSuppression
|
|
46
|
-
*/ _define_property(this, "suppressionElement", void 0);
|
|
47
47
|
}
|
|
48
48
|
};
|
package/main/rules/APIVersion.js
CHANGED
|
@@ -106,8 +106,9 @@ let APIVersion = class APIVersion extends _RuleCommon.RuleCommon {
|
|
|
106
106
|
}
|
|
107
107
|
constructor(){
|
|
108
108
|
super({
|
|
109
|
+
ruleId: "invalid-api-version",
|
|
109
110
|
name: "APIVersion",
|
|
110
|
-
label: "
|
|
111
|
+
label: "Invalid API Version",
|
|
111
112
|
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.",
|
|
112
113
|
supportedTypes: _internals.FlowType.allTypes(),
|
|
113
114
|
docRefs: []
|
|
@@ -19,6 +19,7 @@ let ActionCallsInLoop = class ActionCallsInLoop extends _LoopRuleCommon.LoopRule
|
|
|
19
19
|
}
|
|
20
20
|
constructor(){
|
|
21
21
|
super({
|
|
22
|
+
ruleId: "action-call-in-loop",
|
|
22
23
|
description: "To prevent exceeding Apex governor limits, it is advisable to consolidate and bulkify your apex calls, utilize a single action call containing a collection variable at the end of the loop.",
|
|
23
24
|
docRefs: [
|
|
24
25
|
{
|
package/main/rules/AutoLayout.js
CHANGED
|
@@ -65,6 +65,7 @@ let AutoLayout = class AutoLayout extends _RuleCommon.RuleCommon {
|
|
|
65
65
|
}
|
|
66
66
|
constructor(){
|
|
67
67
|
super({
|
|
68
|
+
ruleId: "missing-auto-layout",
|
|
68
69
|
name: "AutoLayout",
|
|
69
70
|
label: "Auto-Layout Mode",
|
|
70
71
|
description: "With Canvas Mode set to Auto-Layout, Elements are spaced, connected, and aligned automatically, keeping your Flow neatly organized thus saving you time.",
|
|
@@ -59,8 +59,9 @@ let CopyAPIName = class CopyAPIName extends _RuleCommon.RuleCommon {
|
|
|
59
59
|
}
|
|
60
60
|
constructor(){
|
|
61
61
|
super({
|
|
62
|
+
ruleId: "unclear-api-naming",
|
|
62
63
|
name: "CopyAPIName",
|
|
63
|
-
label: "
|
|
64
|
+
label: "Unclear API Name",
|
|
64
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, it's crucial to remember to update the API name of the newly created copy.",
|
|
65
66
|
supportedTypes: _internals.FlowType.allTypes(),
|
|
66
67
|
docRefs: []
|
|
@@ -87,8 +87,9 @@ let CyclomaticComplexity = class CyclomaticComplexity extends _RuleCommon.RuleCo
|
|
|
87
87
|
}
|
|
88
88
|
constructor(){
|
|
89
89
|
super({
|
|
90
|
+
ruleId: "excessive-cyclomatic-complexity",
|
|
90
91
|
name: "CyclomaticComplexity",
|
|
91
|
-
label: "Cyclomatic Complexity",
|
|
92
|
+
label: "Excessive Cyclomatic Complexity",
|
|
92
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
94
|
supportedTypes: _internals.FlowType.backEndTypes,
|
|
94
95
|
docRefs: [
|
|
@@ -20,6 +20,7 @@ let DMLStatementInLoop = class DMLStatementInLoop extends _LoopRuleCommon.LoopRu
|
|
|
20
20
|
}
|
|
21
21
|
constructor(){
|
|
22
22
|
super({
|
|
23
|
+
ruleId: "dml-in-loop",
|
|
23
24
|
description: "To prevent exceeding Apex governor limits, it is advisable to consolidate all your database operations, including record creation, updates, or deletions, at the conclusion of the flow.",
|
|
24
25
|
docRefs: [
|
|
25
26
|
{
|
|
@@ -94,6 +94,7 @@ let DuplicateDMLOperation = class DuplicateDMLOperation extends _RuleCommon.Rule
|
|
|
94
94
|
}
|
|
95
95
|
constructor(){
|
|
96
96
|
super({
|
|
97
|
+
ruleId: "duplicate-dml",
|
|
97
98
|
name: "DuplicateDMLOperation",
|
|
98
99
|
label: "Duplicate DML Operation",
|
|
99
100
|
description: "When the flow executes database changes between screens, users must not be allowed to navigate back, or duplicate DML operations may occur.",
|
|
@@ -63,6 +63,7 @@ let FlowDescription = class FlowDescription extends _RuleCommon.RuleCommon {
|
|
|
63
63
|
}
|
|
64
64
|
constructor(){
|
|
65
65
|
super({
|
|
66
|
+
ruleId: "missing-flow-description",
|
|
66
67
|
description: "Descriptions play a vital role in documentation. We highly recommend including details about where they are used and their intended purpose.",
|
|
67
68
|
docRefs: [],
|
|
68
69
|
label: "Missing Flow Description",
|
package/main/rules/FlowName.js
CHANGED
|
@@ -66,6 +66,7 @@ let FlowName = class FlowName extends _RuleCommon.RuleCommon {
|
|
|
66
66
|
}
|
|
67
67
|
constructor(){
|
|
68
68
|
super({
|
|
69
|
+
ruleId: "invalid-naming-convention",
|
|
69
70
|
description: "The readability of a flow is of utmost importance. Establishing a naming convention for the Flow Name significantly enhances findability, searchability, and maintains overall consistency. It is advisable to include at least a domain and a brief description of the actions carried out in the flow, for instance, 'Service_OrderFulfillment'.",
|
|
70
71
|
docRefs: [
|
|
71
72
|
{
|
|
@@ -66,6 +66,7 @@ let GetRecordAllFields = class GetRecordAllFields extends _RuleCommon.RuleCommon
|
|
|
66
66
|
}
|
|
67
67
|
constructor(){
|
|
68
68
|
super({
|
|
69
|
+
ruleId: "get-record-all-fields",
|
|
69
70
|
description: "Following the principle of least privilege (PoLP), avoid using Get Records with 'Automatically store all fields' unless necessary.",
|
|
70
71
|
docRefs: [
|
|
71
72
|
{
|
|
@@ -58,6 +58,7 @@ let HardcodedId = class HardcodedId extends _RuleCommon.RuleCommon {
|
|
|
58
58
|
}
|
|
59
59
|
constructor(){
|
|
60
60
|
super({
|
|
61
|
+
ruleId: "hardcoded-id",
|
|
61
62
|
name: "HardcodedId",
|
|
62
63
|
label: "Hardcoded Id",
|
|
63
64
|
description: "Avoid hard-coding IDs as they are org-specific. Instead, pass them into variables at the start of the flow. You can achieve this by utilizing merge fields in URL parameters or employing a Get Records element.",
|
|
@@ -18,6 +18,7 @@ let HardcodedUrl = class HardcodedUrl extends _RuleCommon.RuleCommon {
|
|
|
18
18
|
}
|
|
19
19
|
constructor(){
|
|
20
20
|
super({
|
|
21
|
+
ruleId: "hardcoded-url",
|
|
21
22
|
description: "Avoid hard-coding URLs as they are org-specific. Instead, use a $API formula (preferred) or you can use an environment-specific such as custom labels, custom metadata, or custom settings.",
|
|
22
23
|
docRefs: [
|
|
23
24
|
{
|
|
@@ -62,6 +62,7 @@ let InactiveFlow = class InactiveFlow extends _RuleCommon.RuleCommon {
|
|
|
62
62
|
}
|
|
63
63
|
constructor(){
|
|
64
64
|
super({
|
|
65
|
+
ruleId: "inactive-flow",
|
|
65
66
|
name: "InactiveFlow",
|
|
66
67
|
label: "Inactive Flow",
|
|
67
68
|
description: "Like cleaning out your closet: deleting unused flows is essential. Inactive flows can still cause trouble, like accidentally deleting records during testing, or being activated as subflows within parent flows.",
|
|
@@ -126,6 +126,7 @@ let MissingFaultPath = class MissingFaultPath extends _RuleCommon.RuleCommon {
|
|
|
126
126
|
}
|
|
127
127
|
constructor(){
|
|
128
128
|
super({
|
|
129
|
+
ruleId: "missing-fault-path",
|
|
129
130
|
description: "At times, a flow may fail to execute a configured operation as intended. By default, the flow displays an error message to the user and notifies the admin who created the flow via email. However, you can customize this behavior by incorporating a Fault Path. This rule checks DML operations, actions (Send Email, Quick Actions), and Invocable Apex Actions for proper error handling.",
|
|
130
131
|
docRefs: [
|
|
131
132
|
{
|
|
@@ -65,6 +65,7 @@ let MissingMetadataDescription = class MissingMetadataDescription extends _RuleC
|
|
|
65
65
|
}
|
|
66
66
|
constructor(){
|
|
67
67
|
super({
|
|
68
|
+
ruleId: "missing-metadata-description",
|
|
68
69
|
description: "Every element must have a meaningful description",
|
|
69
70
|
docRefs: [],
|
|
70
71
|
label: "Missing Metadata Description",
|
|
@@ -130,6 +130,7 @@ let MissingNullHandler = class MissingNullHandler extends _RuleCommon.RuleCommon
|
|
|
130
130
|
}
|
|
131
131
|
constructor(){
|
|
132
132
|
super({
|
|
133
|
+
ruleId: "missing-null-handler",
|
|
133
134
|
description: "When a Get Records operation doesn't find any data, it returns null. To ensure data validation, utilize a decision element on the operation result variable to check for a non-null result.",
|
|
134
135
|
docRefs: [],
|
|
135
136
|
label: "Missing Null Handler",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as core from "../internals/internals";
|
|
2
2
|
import { RuleCommon } from "../models/RuleCommon";
|
|
3
3
|
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
|
|
4
|
-
export declare class
|
|
4
|
+
export declare class MissingRecordTriggerFilter extends RuleCommon implements IRuleDefinition {
|
|
5
5
|
constructor();
|
|
6
6
|
protected check(flow: core.Flow, _options: object | undefined, _suppressions: Set<string>): core.Violation[];
|
|
7
7
|
}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
Object.defineProperty(exports, "
|
|
5
|
+
Object.defineProperty(exports, "MissingRecordTriggerFilter", {
|
|
6
6
|
enumerable: true,
|
|
7
7
|
get: function() {
|
|
8
|
-
return
|
|
8
|
+
return MissingRecordTriggerFilter;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
11
|
const _internals = /*#__PURE__*/ _interop_require_wildcard(require("../internals/internals"));
|
|
@@ -51,7 +51,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
51
51
|
}
|
|
52
52
|
return newObj;
|
|
53
53
|
}
|
|
54
|
-
let
|
|
54
|
+
let MissingRecordTriggerFilter = class MissingRecordTriggerFilter extends _RuleCommon.RuleCommon {
|
|
55
55
|
check(flow, _options, _suppressions) {
|
|
56
56
|
var _flow_xmldata_start, _flow_xmldata;
|
|
57
57
|
const violations = [];
|
|
@@ -77,7 +77,8 @@ let MissingFilterRecordTrigger = class MissingFilterRecordTrigger extends _RuleC
|
|
|
77
77
|
}
|
|
78
78
|
constructor(){
|
|
79
79
|
super({
|
|
80
|
-
|
|
80
|
+
ruleId: "missing-record-trigger-filter",
|
|
81
|
+
name: "MissingRecordTriggerFilter",
|
|
81
82
|
label: "Missing Record Trigger Filter",
|
|
82
83
|
description: "Detects record-triggered flows that lack filters on changed fields or entry conditions, leading to unnecessary executions on every record change. This can degrade system performance, hit governor limits faster, and increase resource consumption in high-volume orgs.",
|
|
83
84
|
supportedTypes: [
|
|
@@ -59,8 +59,9 @@ let ProcessBuilder = class ProcessBuilder extends _RuleCommon.RuleCommon {
|
|
|
59
59
|
}
|
|
60
60
|
constructor(){
|
|
61
61
|
super({
|
|
62
|
+
ruleId: "process-builder-usage",
|
|
62
63
|
name: "ProcessBuilder",
|
|
63
|
-
label: "
|
|
64
|
+
label: "Process Builder Usage",
|
|
64
65
|
description: "Salesforce is transitioning away from Workflow Rules and Process Builder in favor of Flow. Ensure you're prepared for this transition by migrating your organization's automation to Flow. Refer to official documentation for more information on the transition process and tools available.",
|
|
65
66
|
supportedTypes: _internals.FlowType.processBuilder,
|
|
66
67
|
docRefs: [
|
|
@@ -73,6 +73,7 @@ let RecordIdAsString = class RecordIdAsString extends _RuleCommon.RuleCommon {
|
|
|
73
73
|
}
|
|
74
74
|
constructor(){
|
|
75
75
|
super({
|
|
76
|
+
ruleId: "record-id-as-string",
|
|
76
77
|
name: "RecordIdAsString",
|
|
77
78
|
label: "Record ID as String Instead of Record",
|
|
78
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.",
|
|
@@ -104,6 +104,7 @@ let RecursiveAfterUpdate = class RecursiveAfterUpdate extends _RuleCommon.RuleCo
|
|
|
104
104
|
}
|
|
105
105
|
constructor(){
|
|
106
106
|
super({
|
|
107
|
+
ruleId: "recursive-record-update",
|
|
107
108
|
description: "After updates are meant to be used for record modifications that are not the same record that triggered the flow. Using after updates on the same record can lead to recursion and unexpected behavior. Consider using before save flows for same record updates.",
|
|
108
109
|
docRefs: [
|
|
109
110
|
{
|
|
@@ -18,6 +18,7 @@ let SOQLQueryInLoop = class SOQLQueryInLoop extends _LoopRuleCommon.LoopRuleComm
|
|
|
18
18
|
}
|
|
19
19
|
constructor(){
|
|
20
20
|
super({
|
|
21
|
+
ruleId: "soql-in-loop",
|
|
21
22
|
description: "To prevent exceeding Apex governor limits, it is advisable to consolidate all your SOQL queries at the conclusion of the flow.",
|
|
22
23
|
docRefs: [
|
|
23
24
|
{
|
|
@@ -86,6 +86,7 @@ let SameRecordFieldUpdates = class SameRecordFieldUpdates extends _RuleCommon.Ru
|
|
|
86
86
|
}
|
|
87
87
|
constructor(){
|
|
88
88
|
super({
|
|
89
|
+
ruleId: "same-record-field-updates",
|
|
89
90
|
name: "SameRecordFieldUpdates",
|
|
90
91
|
label: "Same Record Field Updates",
|
|
91
92
|
description: "Before-save same-record field updates allows you to update the record using variable assignments to `$Record`. This is significantly faster than doing another DML on the same-record that triggered the flow",
|
|
@@ -76,6 +76,7 @@ let TransformInsteadOfLoop = class TransformInsteadOfLoop extends _RuleCommon.Ru
|
|
|
76
76
|
}
|
|
77
77
|
constructor(){
|
|
78
78
|
super({
|
|
79
|
+
ruleId: "transform-instead-of-loop",
|
|
79
80
|
name: "TransformInsteadOfLoop",
|
|
80
81
|
label: "Transform Instead of Loop",
|
|
81
82
|
description: "Detects Loop elements that directly connect to Assignment elements. This pattern can often be replaced with the Transform element, which is on average 10x more performant according to Salesforce documentation.",
|
|
@@ -68,6 +68,7 @@ let TriggerOrder = class TriggerOrder extends _RuleCommon.RuleCommon {
|
|
|
68
68
|
}
|
|
69
69
|
constructor(){
|
|
70
70
|
super({
|
|
71
|
+
ruleId: "unspecified-trigger-order",
|
|
71
72
|
name: "TriggerOrder",
|
|
72
73
|
label: "Trigger Order",
|
|
73
74
|
description: "With flow trigger ordering, introduced in Spring '22, admins can now assign a priority " + "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.",
|
|
@@ -61,9 +61,10 @@ let UnconnectedElement = class UnconnectedElement extends _RuleCommon.RuleCommon
|
|
|
61
61
|
}
|
|
62
62
|
constructor(){
|
|
63
63
|
super({
|
|
64
|
+
ruleId: "unreachable-element",
|
|
64
65
|
description: "To maintain the efficiency and manageability of your Flow, it's best to avoid including unconnected elements that are not in use.",
|
|
65
66
|
docRefs: [],
|
|
66
|
-
label: "
|
|
67
|
+
label: "Unreachable Element",
|
|
67
68
|
name: "UnconnectedElement",
|
|
68
69
|
supportedTypes: [
|
|
69
70
|
..._internals.FlowType.backEndTypes,
|
|
@@ -67,6 +67,7 @@ let UnsafeRunningContext = class UnsafeRunningContext extends _RuleCommon.RuleCo
|
|
|
67
67
|
}
|
|
68
68
|
constructor(){
|
|
69
69
|
super({
|
|
70
|
+
ruleId: "unsafe-running-context",
|
|
70
71
|
name: "UnsafeRunningContext",
|
|
71
72
|
label: "Unsafe Running Context",
|
|
72
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.`,
|
|
@@ -79,6 +79,7 @@ let UnusedVariable = class UnusedVariable extends _RuleCommon.RuleCommon {
|
|
|
79
79
|
}
|
|
80
80
|
constructor(){
|
|
81
81
|
super({
|
|
82
|
+
ruleId: "unused-variable",
|
|
82
83
|
name: "UnusedVariable",
|
|
83
84
|
label: "Unused Variable",
|
|
84
85
|
description: "To maintain the efficiency and manageability of your Flow, it's advisable to avoid including unconnected variables that are not in use.",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
|
|
2
|
+
import { IRulesConfig } from "../interfaces/IRulesConfig";
|
|
3
|
+
type RuleConstructor = new () => IRuleDefinition;
|
|
4
|
+
interface RuleRegistryEntry {
|
|
5
|
+
ruleId: string;
|
|
6
|
+
ruleClass: RuleConstructor;
|
|
7
|
+
legacyName: string;
|
|
8
|
+
isBeta: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare class RuleRegistry {
|
|
11
|
+
private rules;
|
|
12
|
+
private legacyNameMap;
|
|
13
|
+
register(ruleId: string, ruleClass: RuleConstructor, legacyName: string, isBeta?: boolean): void;
|
|
14
|
+
get(idOrLegacyName: string): RuleRegistryEntry | undefined;
|
|
15
|
+
getAllRuleIds(includeBeta?: boolean): string[];
|
|
16
|
+
has(idOrLegacyName: string): boolean;
|
|
17
|
+
createInstance(idOrLegacyName: string): IRuleDefinition;
|
|
18
|
+
getRules(ruleConfig?: Map<string, unknown>, options?: IRulesConfig): IRuleDefinition[];
|
|
19
|
+
getRulesByNames(ruleNames?: string[], options?: IRulesConfig): IRuleDefinition[];
|
|
20
|
+
}
|
|
21
|
+
export declare const ruleRegistry: RuleRegistry;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "ruleRegistry", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return ruleRegistry;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _ActionCallsInLoop = require("../rules/ActionCallsInLoop");
|
|
12
|
+
const _APIVersion = require("../rules/APIVersion");
|
|
13
|
+
const _AutoLayout = require("../rules/AutoLayout");
|
|
14
|
+
const _CopyAPIName = require("../rules/CopyAPIName");
|
|
15
|
+
const _CyclomaticComplexity = require("../rules/CyclomaticComplexity");
|
|
16
|
+
const _DMLStatementInLoop = require("../rules/DMLStatementInLoop");
|
|
17
|
+
const _DuplicateDMLOperation = require("../rules/DuplicateDMLOperation");
|
|
18
|
+
const _FlowDescription = require("../rules/FlowDescription");
|
|
19
|
+
const _FlowName = require("../rules/FlowName");
|
|
20
|
+
const _GetRecordAllFields = require("../rules/GetRecordAllFields");
|
|
21
|
+
const _HardcodedId = require("../rules/HardcodedId");
|
|
22
|
+
const _HardcodedUrl = require("../rules/HardcodedUrl");
|
|
23
|
+
const _InactiveFlow = require("../rules/InactiveFlow");
|
|
24
|
+
const _MissingFaultPath = require("../rules/MissingFaultPath");
|
|
25
|
+
const _MissingNullHandler = require("../rules/MissingNullHandler");
|
|
26
|
+
const _ProcessBuilder = require("../rules/ProcessBuilder");
|
|
27
|
+
const _RecursiveAfterUpdate = require("../rules/RecursiveAfterUpdate");
|
|
28
|
+
const _SameRecordFieldUpdates = require("../rules/SameRecordFieldUpdates");
|
|
29
|
+
const _SOQLQueryInLoop = require("../rules/SOQLQueryInLoop");
|
|
30
|
+
const _TriggerOrder = require("../rules/TriggerOrder");
|
|
31
|
+
const _UnconnectedElement = require("../rules/UnconnectedElement");
|
|
32
|
+
const _UnsafeRunningContext = require("../rules/UnsafeRunningContext");
|
|
33
|
+
const _UnusedVariable = require("../rules/UnusedVariable");
|
|
34
|
+
const _MissingMetadataDescription = require("../rules/MissingMetadataDescription");
|
|
35
|
+
const _MissingRecordTriggerFilter = require("../rules/MissingRecordTriggerFilter");
|
|
36
|
+
const _TransformInsteadOfLoop = require("../rules/TransformInsteadOfLoop");
|
|
37
|
+
const _RecordIdAsString = require("../rules/RecordIdAsString");
|
|
38
|
+
function _define_property(obj, key, value) {
|
|
39
|
+
if (key in obj) {
|
|
40
|
+
Object.defineProperty(obj, key, {
|
|
41
|
+
value: value,
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
writable: true
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
obj[key] = value;
|
|
48
|
+
}
|
|
49
|
+
return obj;
|
|
50
|
+
}
|
|
51
|
+
function _object_spread(target) {
|
|
52
|
+
for(var i = 1; i < arguments.length; i++){
|
|
53
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
54
|
+
var ownKeys = Object.keys(source);
|
|
55
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
56
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
57
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
ownKeys.forEach(function(key) {
|
|
61
|
+
_define_property(target, key, source[key]);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return target;
|
|
65
|
+
}
|
|
66
|
+
function ownKeys(object, enumerableOnly) {
|
|
67
|
+
var keys = Object.keys(object);
|
|
68
|
+
if (Object.getOwnPropertySymbols) {
|
|
69
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
70
|
+
if (enumerableOnly) {
|
|
71
|
+
symbols = symbols.filter(function(sym) {
|
|
72
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
keys.push.apply(keys, symbols);
|
|
76
|
+
}
|
|
77
|
+
return keys;
|
|
78
|
+
}
|
|
79
|
+
function _object_spread_props(target, source) {
|
|
80
|
+
source = source != null ? source : {};
|
|
81
|
+
if (Object.getOwnPropertyDescriptors) {
|
|
82
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
83
|
+
} else {
|
|
84
|
+
ownKeys(Object(source)).forEach(function(key) {
|
|
85
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return target;
|
|
89
|
+
}
|
|
90
|
+
let RuleRegistry = class RuleRegistry {
|
|
91
|
+
register(ruleId, ruleClass, legacyName, isBeta = false) {
|
|
92
|
+
const entry = {
|
|
93
|
+
ruleId,
|
|
94
|
+
ruleClass,
|
|
95
|
+
legacyName,
|
|
96
|
+
isBeta
|
|
97
|
+
};
|
|
98
|
+
this.rules.set(ruleId, entry);
|
|
99
|
+
this.legacyNameMap.set(legacyName, ruleId);
|
|
100
|
+
}
|
|
101
|
+
get(idOrLegacyName) {
|
|
102
|
+
let entry = this.rules.get(idOrLegacyName);
|
|
103
|
+
if (!entry) {
|
|
104
|
+
const ruleId = this.legacyNameMap.get(idOrLegacyName);
|
|
105
|
+
if (ruleId) {
|
|
106
|
+
entry = this.rules.get(ruleId);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return entry;
|
|
110
|
+
}
|
|
111
|
+
getAllRuleIds(includeBeta = false) {
|
|
112
|
+
return Array.from(this.rules.values()).filter((entry)=>includeBeta || !entry.isBeta).map((entry)=>entry.ruleId);
|
|
113
|
+
}
|
|
114
|
+
has(idOrLegacyName) {
|
|
115
|
+
return this.get(idOrLegacyName) !== undefined;
|
|
116
|
+
}
|
|
117
|
+
createInstance(idOrLegacyName) {
|
|
118
|
+
const entry = this.get(idOrLegacyName);
|
|
119
|
+
if (!entry) {
|
|
120
|
+
throw new Error(`Rule not found: ${idOrLegacyName}`);
|
|
121
|
+
}
|
|
122
|
+
return new entry.ruleClass();
|
|
123
|
+
}
|
|
124
|
+
getRules(ruleConfig, options) {
|
|
125
|
+
const includeBeta = (options === null || options === void 0 ? void 0 : options.betaMode) === true || (options === null || options === void 0 ? void 0 : options.betamode) === true;
|
|
126
|
+
const rulesMode = (options === null || options === void 0 ? void 0 : options.ruleMode) || "merged";
|
|
127
|
+
const selectedRules = [];
|
|
128
|
+
if (rulesMode === "isolated" && ruleConfig && ruleConfig.size > 0) {
|
|
129
|
+
for (const key of ruleConfig.keys()){
|
|
130
|
+
const entry = this.get(key);
|
|
131
|
+
if (!entry) continue;
|
|
132
|
+
const config = ruleConfig.get(key);
|
|
133
|
+
if ((config === null || config === void 0 ? void 0 : config.enabled) === false) continue;
|
|
134
|
+
const rule = this.createInstance(entry.ruleId);
|
|
135
|
+
if (config === null || config === void 0 ? void 0 : config.severity) {
|
|
136
|
+
rule.severity = config.severity;
|
|
137
|
+
}
|
|
138
|
+
selectedRules.push(rule);
|
|
139
|
+
}
|
|
140
|
+
return selectedRules;
|
|
141
|
+
}
|
|
142
|
+
const allRuleIds = this.getAllRuleIds(includeBeta);
|
|
143
|
+
for (const ruleId of allRuleIds){
|
|
144
|
+
const rule = this.createInstance(ruleId);
|
|
145
|
+
var _ruleConfig_get;
|
|
146
|
+
const config = (_ruleConfig_get = ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.get(rule.ruleId)) !== null && _ruleConfig_get !== void 0 ? _ruleConfig_get : ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.get(rule.name);
|
|
147
|
+
if ((config === null || config === void 0 ? void 0 : config.enabled) === false) continue;
|
|
148
|
+
if (config === null || config === void 0 ? void 0 : config.severity) {
|
|
149
|
+
rule.severity = config.severity;
|
|
150
|
+
}
|
|
151
|
+
selectedRules.push(rule);
|
|
152
|
+
}
|
|
153
|
+
return selectedRules;
|
|
154
|
+
}
|
|
155
|
+
getRulesByNames(ruleNames, options) {
|
|
156
|
+
if (!ruleNames || ruleNames.length === 0) {
|
|
157
|
+
return this.getRules(undefined, options);
|
|
158
|
+
}
|
|
159
|
+
const config = new Map();
|
|
160
|
+
for (const identifier of ruleNames){
|
|
161
|
+
const entry = this.get(identifier);
|
|
162
|
+
if (entry) {
|
|
163
|
+
config.set(entry.ruleId, {
|
|
164
|
+
enabled: true
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return this.getRules(config, _object_spread_props(_object_spread({}, options), {
|
|
169
|
+
ruleMode: "isolated"
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
constructor(){
|
|
173
|
+
_define_property(this, "rules", new Map());
|
|
174
|
+
_define_property(this, "legacyNameMap", new Map());
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const registry = new RuleRegistry();
|
|
178
|
+
registry.register("action-call-in-loop", _ActionCallsInLoop.ActionCallsInLoop, "ActionCallsInLoop");
|
|
179
|
+
registry.register("invalid-api-version", _APIVersion.APIVersion, "APIVersion");
|
|
180
|
+
registry.register("missing-auto-layout", _AutoLayout.AutoLayout, "AutoLayout");
|
|
181
|
+
registry.register("unclear-api-naming", _CopyAPIName.CopyAPIName, "CopyAPIName");
|
|
182
|
+
registry.register("excessive-cyclomatic-complexity", _CyclomaticComplexity.CyclomaticComplexity, "CyclomaticComplexity");
|
|
183
|
+
registry.register("dml-in-loop", _DMLStatementInLoop.DMLStatementInLoop, "DMLStatementInLoop");
|
|
184
|
+
registry.register("duplicate-dml", _DuplicateDMLOperation.DuplicateDMLOperation, "DuplicateDMLOperation");
|
|
185
|
+
registry.register("missing-flow-description", _FlowDescription.FlowDescription, "FlowDescription");
|
|
186
|
+
registry.register("invalid-naming-convention", _FlowName.FlowName, "FlowName");
|
|
187
|
+
registry.register("get-record-all-fields", _GetRecordAllFields.GetRecordAllFields, "GetRecordAllFields");
|
|
188
|
+
registry.register("hardcoded-id", _HardcodedId.HardcodedId, "HardcodedId");
|
|
189
|
+
registry.register("hardcoded-url", _HardcodedUrl.HardcodedUrl, "HardcodedUrl");
|
|
190
|
+
registry.register("inactive-flow", _InactiveFlow.InactiveFlow, "InactiveFlow");
|
|
191
|
+
registry.register("missing-fault-path", _MissingFaultPath.MissingFaultPath, "MissingFaultPath");
|
|
192
|
+
registry.register("missing-null-handler", _MissingNullHandler.MissingNullHandler, "MissingNullHandler");
|
|
193
|
+
registry.register("process-builder-usage", _ProcessBuilder.ProcessBuilder, "ProcessBuilder");
|
|
194
|
+
registry.register("recursive-record-update", _RecursiveAfterUpdate.RecursiveAfterUpdate, "RecursiveAfterUpdate");
|
|
195
|
+
registry.register("same-record-field-updates", _SameRecordFieldUpdates.SameRecordFieldUpdates, "SameRecordFieldUpdates");
|
|
196
|
+
registry.register("soql-in-loop", _SOQLQueryInLoop.SOQLQueryInLoop, "SOQLQueryInLoop");
|
|
197
|
+
registry.register("unspecified-trigger-order", _TriggerOrder.TriggerOrder, "TriggerOrder");
|
|
198
|
+
registry.register("unreachable-element", _UnconnectedElement.UnconnectedElement, "UnconnectedElement");
|
|
199
|
+
registry.register("unsafe-running-context", _UnsafeRunningContext.UnsafeRunningContext, "UnsafeRunningContext");
|
|
200
|
+
registry.register("unused-variable", _UnusedVariable.UnusedVariable, "UnusedVariable");
|
|
201
|
+
registry.register("missing-metadata-description", _MissingMetadataDescription.MissingMetadataDescription, "MissingMetadataDescription", true);
|
|
202
|
+
registry.register("missing-record-trigger-filter", _MissingRecordTriggerFilter.MissingRecordTriggerFilter, "MissingFilterRecordTrigger", true);
|
|
203
|
+
registry.register("transform-instead-of-loop", _TransformInsteadOfLoop.TransformInsteadOfLoop, "TransformInsteadOfLoop", true);
|
|
204
|
+
registry.register("record-id-as-string", _RecordIdAsString.RecordIdAsString, "RecordIdAsString", true);
|
|
205
|
+
const ruleRegistry = registry;
|
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.11.
|
|
4
|
+
"version": "6.11.5",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
package/main/libs/DynamicRule.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "DynamicRule", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return DynamicRule;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _DefaultRuleStore = require("../store/DefaultRuleStore");
|
|
12
|
-
let DynamicRule = class DynamicRule {
|
|
13
|
-
constructor(className, betaMode = false){
|
|
14
|
-
if (betaMode && _DefaultRuleStore.BetaRuleStore.hasOwnProperty(className)) {
|
|
15
|
-
return new _DefaultRuleStore.BetaRuleStore[className]();
|
|
16
|
-
}
|
|
17
|
-
return new _DefaultRuleStore.DefaultRuleStore[className]();
|
|
18
|
-
}
|
|
19
|
-
};
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get BetaRuleStore () {
|
|
13
|
-
return BetaRuleStore;
|
|
14
|
-
},
|
|
15
|
-
get DefaultRuleStore () {
|
|
16
|
-
return DefaultRuleStore;
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
const _ActionCallsInLoop = require("../rules/ActionCallsInLoop");
|
|
20
|
-
const _APIVersion = require("../rules/APIVersion");
|
|
21
|
-
const _AutoLayout = require("../rules/AutoLayout");
|
|
22
|
-
const _CopyAPIName = require("../rules/CopyAPIName");
|
|
23
|
-
const _CyclomaticComplexity = require("../rules/CyclomaticComplexity");
|
|
24
|
-
const _DMLStatementInLoop = require("../rules/DMLStatementInLoop");
|
|
25
|
-
const _DuplicateDMLOperation = require("../rules/DuplicateDMLOperation");
|
|
26
|
-
const _FlowDescription = require("../rules/FlowDescription");
|
|
27
|
-
const _FlowName = require("../rules/FlowName");
|
|
28
|
-
const _GetRecordAllFields = require("../rules/GetRecordAllFields");
|
|
29
|
-
const _HardcodedId = require("../rules/HardcodedId");
|
|
30
|
-
const _HardcodedUrl = require("../rules/HardcodedUrl");
|
|
31
|
-
const _InactiveFlow = require("../rules/InactiveFlow");
|
|
32
|
-
const _MissingFaultPath = require("../rules/MissingFaultPath");
|
|
33
|
-
const _MissingNullHandler = require("../rules/MissingNullHandler");
|
|
34
|
-
const _ProcessBuilder = require("../rules/ProcessBuilder");
|
|
35
|
-
const _RecursiveAfterUpdate = require("../rules/RecursiveAfterUpdate");
|
|
36
|
-
const _SameRecordFieldUpdates = require("../rules/SameRecordFieldUpdates");
|
|
37
|
-
const _SOQLQueryInLoop = require("../rules/SOQLQueryInLoop");
|
|
38
|
-
const _TriggerOrder = require("../rules/TriggerOrder");
|
|
39
|
-
const _UnconnectedElement = require("../rules/UnconnectedElement");
|
|
40
|
-
const _UnsafeRunningContext = require("../rules/UnsafeRunningContext");
|
|
41
|
-
const _UnusedVariable = require("../rules/UnusedVariable");
|
|
42
|
-
const _MissingMetadataDescription = require("../rules/MissingMetadataDescription");
|
|
43
|
-
const _MissingFilterRecordTrigger = require("../rules/MissingFilterRecordTrigger");
|
|
44
|
-
const _TransformInsteadOfLoop = require("../rules/TransformInsteadOfLoop");
|
|
45
|
-
const _RecordIdAsString = require("../rules/RecordIdAsString");
|
|
46
|
-
const DefaultRuleStore = {
|
|
47
|
-
ActionCallsInLoop: _ActionCallsInLoop.ActionCallsInLoop,
|
|
48
|
-
APIVersion: _APIVersion.APIVersion,
|
|
49
|
-
AutoLayout: _AutoLayout.AutoLayout,
|
|
50
|
-
CopyAPIName: _CopyAPIName.CopyAPIName,
|
|
51
|
-
CyclomaticComplexity: _CyclomaticComplexity.CyclomaticComplexity,
|
|
52
|
-
DMLStatementInLoop: _DMLStatementInLoop.DMLStatementInLoop,
|
|
53
|
-
DuplicateDMLOperation: _DuplicateDMLOperation.DuplicateDMLOperation,
|
|
54
|
-
FlowDescription: _FlowDescription.FlowDescription,
|
|
55
|
-
FlowName: _FlowName.FlowName,
|
|
56
|
-
GetRecordAllFields: _GetRecordAllFields.GetRecordAllFields,
|
|
57
|
-
HardcodedId: _HardcodedId.HardcodedId,
|
|
58
|
-
HardcodedUrl: _HardcodedUrl.HardcodedUrl,
|
|
59
|
-
InactiveFlow: _InactiveFlow.InactiveFlow,
|
|
60
|
-
MissingFaultPath: _MissingFaultPath.MissingFaultPath,
|
|
61
|
-
MissingNullHandler: _MissingNullHandler.MissingNullHandler,
|
|
62
|
-
ProcessBuilder: _ProcessBuilder.ProcessBuilder,
|
|
63
|
-
RecursiveAfterUpdate: _RecursiveAfterUpdate.RecursiveAfterUpdate,
|
|
64
|
-
SameRecordFieldUpdates: _SameRecordFieldUpdates.SameRecordFieldUpdates,
|
|
65
|
-
SOQLQueryInLoop: _SOQLQueryInLoop.SOQLQueryInLoop,
|
|
66
|
-
TriggerOrder: _TriggerOrder.TriggerOrder,
|
|
67
|
-
UnconnectedElement: _UnconnectedElement.UnconnectedElement,
|
|
68
|
-
UnsafeRunningContext: _UnsafeRunningContext.UnsafeRunningContext,
|
|
69
|
-
UnusedVariable: _UnusedVariable.UnusedVariable
|
|
70
|
-
};
|
|
71
|
-
const BetaRuleStore = {
|
|
72
|
-
MissingMetadataDescription: _MissingMetadataDescription.MissingMetadataDescription,
|
|
73
|
-
MissingFilterRecordTrigger: _MissingFilterRecordTrigger.MissingFilterRecordTrigger,
|
|
74
|
-
TransformInsteadOfLoop: _TransformInsteadOfLoop.TransformInsteadOfLoop,
|
|
75
|
-
RecordIdAsString: _RecordIdAsString.RecordIdAsString
|
|
76
|
-
};
|