@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.
Files changed (44) hide show
  1. package/README.md +40 -13
  2. package/main/interfaces/IRuleConfig.d.ts +2 -1
  3. package/main/interfaces/IRuleDefinition.d.ts +1 -0
  4. package/main/libs/GetRuleDefinitions.js +26 -63
  5. package/main/libs/ScanFlows.js +17 -9
  6. package/main/models/RuleCommon.d.ts +1 -1
  7. package/main/models/RuleCommon.js +5 -2
  8. package/main/models/RuleInfo.d.ts +6 -6
  9. package/main/models/RuleInfo.js +5 -5
  10. package/main/rules/APIVersion.js +2 -1
  11. package/main/rules/ActionCallsInLoop.js +1 -0
  12. package/main/rules/AutoLayout.js +1 -0
  13. package/main/rules/CopyAPIName.js +2 -1
  14. package/main/rules/CyclomaticComplexity.js +2 -1
  15. package/main/rules/DMLStatementInLoop.js +1 -0
  16. package/main/rules/DuplicateDMLOperation.js +1 -0
  17. package/main/rules/FlowDescription.js +1 -0
  18. package/main/rules/FlowName.js +1 -0
  19. package/main/rules/GetRecordAllFields.js +1 -0
  20. package/main/rules/HardcodedId.js +1 -0
  21. package/main/rules/HardcodedUrl.js +1 -0
  22. package/main/rules/InactiveFlow.js +1 -0
  23. package/main/rules/MissingFaultPath.js +1 -0
  24. package/main/rules/MissingMetadataDescription.js +1 -0
  25. package/main/rules/MissingNullHandler.js +1 -0
  26. package/main/rules/{MissingFilterRecordTrigger.d.ts → MissingRecordTriggerFilter.d.ts} +1 -1
  27. package/main/rules/{MissingFilterRecordTrigger.js → MissingRecordTriggerFilter.js} +5 -4
  28. package/main/rules/ProcessBuilder.js +2 -1
  29. package/main/rules/RecordIdAsString.js +1 -0
  30. package/main/rules/RecursiveAfterUpdate.js +1 -0
  31. package/main/rules/SOQLQueryInLoop.js +1 -0
  32. package/main/rules/SameRecordFieldUpdates.js +1 -0
  33. package/main/rules/TransformInsteadOfLoop.js +1 -0
  34. package/main/rules/TriggerOrder.js +1 -0
  35. package/main/rules/UnconnectedElement.js +2 -1
  36. package/main/rules/UnsafeRunningContext.js +1 -0
  37. package/main/rules/UnusedVariable.js +1 -0
  38. package/main/store/RuleRegistry.d.ts +22 -0
  39. package/main/store/RuleRegistry.js +205 -0
  40. package/package.json +1 -1
  41. package/main/libs/DynamicRule.d.ts +0 -4
  42. package/main/libs/DynamicRule.js +0 -19
  43. package/main/store/DefaultRuleStore.d.ts +0 -2
  44. 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
- ### Outdated API Version
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
- ### Auto Layout
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 Record All Fields
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 ![Beta](https://img.shields.io/badge/status-beta-yellow)
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 ![Beta](https://img.shields.io/badge/status-beta-yellow)
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 ![Beta](https://img.shields.io/badge/status-beta-yellow)
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 ![Beta](https://img.shields.io/badge/status-beta-yellow)
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
- ### Trigger Order
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
  ---
@@ -1,3 +1,4 @@
1
1
  export interface IRuleConfig {
2
- severity?: string;
2
+ enabled?: boolean;
3
+ severity?: "error" | "warning" | "note";
3
4
  }
@@ -1,5 +1,6 @@
1
1
  import { Flow, RuleResult } from "../internals/internals";
2
2
  export interface IRuleDefinition {
3
+ ruleId: string;
3
4
  description: string;
4
5
  docRefs: Array<{
5
6
  label: string;
@@ -16,80 +16,43 @@ _export(exports, {
16
16
  return getRules;
17
17
  }
18
18
  });
19
- const _DefaultRuleStore = require("../store/DefaultRuleStore");
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
- // In "isolated" mode, only load rules that are explicitly configured
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 ruleName of ruleConfig.keys()){
29
- try {
30
- const customConfig = ruleConfig.get(ruleName);
31
- // Skip if explicitly disabled
32
- if (customConfig && customConfig["enabled"] === false) {
33
- continue;
34
- }
35
- // Create the rule instance
36
- const matchedRule = new _DynamicRule.DynamicRule(ruleName, includeBeta);
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
- // In "merged" mode (default), start with all default rules and merge with config
50
- const allRuleNames = new Set();
51
- // Add all default rules
52
- for(const ruleName in _DefaultRuleStore.DefaultRuleStore){
53
- allRuleNames.add(ruleName);
54
- }
55
- // Add beta rules if beta mode is enabled
56
- if (includeBeta) {
57
- for(const ruleName in _DefaultRuleStore.BetaRuleStore){
58
- allRuleNames.add(ruleName);
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
- if (ruleNames && ruleNames.length > 0) {
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
  }
@@ -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
- let config = undefined;
58
- if (ruleOptions === null || ruleOptions === void 0 ? void 0 : (_ruleOptions_rules = ruleOptions.rules) === null || _ruleOptions_rules === void 0 ? void 0 : _ruleOptions_rules[rule.name]) {
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
- * The unique name identifier for the rule.
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
  }
@@ -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
- * The unique name identifier for the rule.
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
  };
@@ -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: "Outdated API Version",
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
  {
@@ -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: "Copy API Name",
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",
@@ -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 MissingFilterRecordTrigger extends RuleCommon implements IRuleDefinition {
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, "MissingFilterRecordTrigger", {
5
+ Object.defineProperty(exports, "MissingRecordTriggerFilter", {
6
6
  enumerable: true,
7
7
  get: function() {
8
- return MissingFilterRecordTrigger;
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 MissingFilterRecordTrigger = class MissingFilterRecordTrigger extends _RuleCommon.RuleCommon {
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
- name: "MissingFilterRecordTrigger",
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: "No Process Builder",
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: "Unconnected Element",
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",
4
+ "version": "6.11.5",
5
5
  "main": "index.js",
6
6
  "exports": {
7
7
  ".": {
@@ -1,4 +0,0 @@
1
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
2
- export declare class DynamicRule<T extends IRuleDefinition> {
3
- constructor(className: string, betaMode?: boolean);
4
- }
@@ -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,2 +0,0 @@
1
- export declare const DefaultRuleStore: object;
2
- export declare const BetaRuleStore: object;
@@ -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
- };