@flow-scanner/lightning-flow-scanner-core 6.5.0 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/index.d.ts +2 -2
- package/index.js +0 -3
- package/main/libs/DynamicRule.d.ts +1 -1
- package/main/libs/DynamicRule.js +2 -2
- package/main/libs/GetRuleDefinitions.d.ts +0 -1
- package/main/libs/GetRuleDefinitions.js +8 -18
- package/main/libs/ScanFlows.js +2 -2
- package/main/rules/MissingMetadataDescription.d.ts +7 -0
- package/main/rules/MissingMetadataDescription.js +77 -0
- package/main/store/DefaultRuleStore.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
<p>📌<strong>Tip:</strong> To link directly to a specific rule, use the full GitHub anchor link format. Example:</p>
|
|
31
31
|
<p><em><a href="https://github.com/Flow-Scanner/lightning-flow-scanner-core#unsafe-running-context">https://github.com/Flow-Scanner/lightning-flow-scanner-core#unsafe-running-context</a></em></p>
|
|
32
32
|
|
|
33
|
-
> Want to code a new rule? → See [How to Write a Rule](docs/write-a-
|
|
33
|
+
> Want to code a new rule? → See [How to Write a Rule](docs/write-a-rule.md)
|
|
34
34
|
|
|
35
|
-
### Action Calls In Loop
|
|
35
|
+
### Action Calls In Loop
|
|
36
36
|
|
|
37
37
|
_[ActionCallsInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner-core/tree/main/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.
|
|
38
38
|
|
|
@@ -88,6 +88,10 @@ _[MissingFaultPath](https://github.com/Flow-Scanner/lightning-flow-scanner-core/
|
|
|
88
88
|
|
|
89
89
|
_[FlowDescription](https://github.com/Flow-Scanner/lightning-flow-scanner-core/tree/main/src/main/rules/FlowDescription.ts)_ - Descriptions play a vital role in documentation. We highly recommend including details about where flows are used and their intended purpose.
|
|
90
90
|
|
|
91
|
+
### MissingMetadataDescription
|
|
92
|
+
|
|
93
|
+
_[MissingMetadataDescription](https://github.com/Flow-Scanner/lightning-flow-scanner-core/tree/main/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.
|
|
94
|
+
|
|
91
95
|
### Missing Null Handler
|
|
92
96
|
|
|
93
97
|
_[MissingNullHandler](https://github.com/Flow-Scanner/lightning-flow-scanner-core/tree/main/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.
|
package/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Compiler } from "./main/libs/Compiler";
|
|
|
5
5
|
import { exportDetails } from "./main/libs/exportAsDetails";
|
|
6
6
|
import { exportSarif } from "./main/libs/exportAsSarif";
|
|
7
7
|
import { fix } from "./main/libs/FixFlows";
|
|
8
|
-
import {
|
|
8
|
+
import { getRules } from "./main/libs/GetRuleDefinitions";
|
|
9
9
|
import { parse } from "./main/libs/ParseFlows";
|
|
10
10
|
import { scan } from "./main/libs/ScanFlows";
|
|
11
11
|
import { Flow } from "./main/models/Flow";
|
|
@@ -19,5 +19,5 @@ import { ParsedFlow } from "./main/models/ParsedFlow";
|
|
|
19
19
|
import { RuleResult } from "./main/models/RuleResult";
|
|
20
20
|
import { ScanResult } from "./main/models/ScanResult";
|
|
21
21
|
import { Violation } from "./main/models/Violation";
|
|
22
|
-
export { Compiler, exportDetails, exportSarif, fix, Flow, FlowAttribute, FlowElement, FlowNode, FlowResource, FlowType, FlowVariable,
|
|
22
|
+
export { Compiler, exportDetails, exportSarif, fix, Flow, FlowAttribute, FlowElement, FlowNode, FlowResource, FlowType, FlowVariable, getRules, parse, ParsedFlow, Violation, RuleResult, scan, ScanResult, };
|
|
23
23
|
export type { FlatViolation, IRuleDefinition, IRulesConfig };
|
package/index.js
CHANGED
package/main/libs/DynamicRule.js
CHANGED
|
@@ -10,8 +10,8 @@ Object.defineProperty(exports, "DynamicRule", {
|
|
|
10
10
|
});
|
|
11
11
|
const _DefaultRuleStore = require("../store/DefaultRuleStore");
|
|
12
12
|
let DynamicRule = class DynamicRule {
|
|
13
|
-
constructor(className){
|
|
14
|
-
if (
|
|
13
|
+
constructor(className, betaMode = false){
|
|
14
|
+
if (betaMode && _DefaultRuleStore.BetaRuleStore.hasOwnProperty(className)) {
|
|
15
15
|
return new _DefaultRuleStore.BetaRuleStore[className]();
|
|
16
16
|
}
|
|
17
17
|
return new _DefaultRuleStore.DefaultRuleStore[className]();
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
|
|
2
2
|
import { IRulesConfig } from "../interfaces/IRulesConfig";
|
|
3
|
-
export declare function getBetaRules(): IRuleDefinition[];
|
|
4
3
|
export declare function GetRuleDefinitions(ruleConfig?: Map<string, unknown>, options?: IRulesConfig): IRuleDefinition[];
|
|
5
4
|
export declare function getRules(ruleNames?: string[], options?: IRulesConfig): IRuleDefinition[];
|
|
@@ -12,18 +12,12 @@ _export(exports, {
|
|
|
12
12
|
get GetRuleDefinitions () {
|
|
13
13
|
return GetRuleDefinitions;
|
|
14
14
|
},
|
|
15
|
-
get getBetaRules () {
|
|
16
|
-
return getBetaRules;
|
|
17
|
-
},
|
|
18
15
|
get getRules () {
|
|
19
16
|
return getRules;
|
|
20
17
|
}
|
|
21
18
|
});
|
|
22
19
|
const _DefaultRuleStore = require("../store/DefaultRuleStore");
|
|
23
20
|
const _DynamicRule = require("./DynamicRule");
|
|
24
|
-
function getBetaRules() {
|
|
25
|
-
return getBetaDefinition();
|
|
26
|
-
}
|
|
27
21
|
function GetRuleDefinitions(ruleConfig, options) {
|
|
28
22
|
const selectedRules = [];
|
|
29
23
|
const includeBeta = (options === null || options === void 0 ? void 0 : options.betaMode) === true || (options === null || options === void 0 ? void 0 : options.betamode) === true;
|
|
@@ -36,10 +30,9 @@ function GetRuleDefinitions(ruleConfig, options) {
|
|
|
36
30
|
if (configuredSeverity && (configuredSeverity === "error" || configuredSeverity === "warning" || configuredSeverity === "note")) {
|
|
37
31
|
severity = configuredSeverity;
|
|
38
32
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
33
|
+
// Pass betaMode to DynamicRule
|
|
34
|
+
const matchedRule = new _DynamicRule.DynamicRule(ruleName, includeBeta);
|
|
35
|
+
if (configuredSeverity) matchedRule.severity = severity;
|
|
43
36
|
selectedRules.push(matchedRule);
|
|
44
37
|
} catch (error) {
|
|
45
38
|
console.log(error.message);
|
|
@@ -48,14 +41,15 @@ function GetRuleDefinitions(ruleConfig, options) {
|
|
|
48
41
|
} else {
|
|
49
42
|
// Load all defaults
|
|
50
43
|
for(const rule in _DefaultRuleStore.DefaultRuleStore){
|
|
51
|
-
const matchedRule = new _DynamicRule.DynamicRule(rule);
|
|
44
|
+
const matchedRule = new _DynamicRule.DynamicRule(rule, includeBeta);
|
|
52
45
|
selectedRules.push(matchedRule);
|
|
53
46
|
}
|
|
54
47
|
}
|
|
55
|
-
|
|
48
|
+
// Optionally add beta-only rules that are not in defaults
|
|
49
|
+
if (includeBeta) {
|
|
56
50
|
for(const betaRuleName in _DefaultRuleStore.BetaRuleStore){
|
|
57
51
|
if (!selectedRules.some((r)=>r.name === betaRuleName)) {
|
|
58
|
-
const betaRule = new _DynamicRule.DynamicRule(betaRuleName);
|
|
52
|
+
const betaRule = new _DynamicRule.DynamicRule(betaRuleName, true);
|
|
59
53
|
selectedRules.push(betaRule);
|
|
60
54
|
}
|
|
61
55
|
}
|
|
@@ -71,10 +65,6 @@ function getRules(ruleNames, options) {
|
|
|
71
65
|
}
|
|
72
66
|
]));
|
|
73
67
|
return GetRuleDefinitions(ruleSeverityMap, options);
|
|
74
|
-
} else {
|
|
75
|
-
return GetRuleDefinitions(undefined, options);
|
|
76
68
|
}
|
|
77
|
-
|
|
78
|
-
function getBetaDefinition() {
|
|
79
|
-
return Object.values(_DefaultRuleStore.BetaRuleStore).map((rule)=>new rule());
|
|
69
|
+
return GetRuleDefinitions(undefined, options);
|
|
80
70
|
}
|
package/main/libs/ScanFlows.js
CHANGED
|
@@ -40,9 +40,9 @@ function ScanFlows(flows, ruleOptions) {
|
|
|
40
40
|
for (const [ruleName, config] of Object.entries(ruleOptions.rules)){
|
|
41
41
|
ruleMap.set(ruleName, config);
|
|
42
42
|
}
|
|
43
|
-
selectedRules = (0, _GetRuleDefinitions.GetRuleDefinitions)(ruleMap);
|
|
43
|
+
selectedRules = (0, _GetRuleDefinitions.GetRuleDefinitions)(ruleMap, ruleOptions);
|
|
44
44
|
} else {
|
|
45
|
-
selectedRules = (0, _GetRuleDefinitions.GetRuleDefinitions)();
|
|
45
|
+
selectedRules = (0, _GetRuleDefinitions.GetRuleDefinitions)(undefined, ruleOptions);
|
|
46
46
|
}
|
|
47
47
|
const flowXmlCache = new Map();
|
|
48
48
|
for (const flowInput of flows){
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
|
|
2
|
+
import * as core from "../internals/internals";
|
|
3
|
+
import { RuleCommon } from "../models/RuleCommon";
|
|
4
|
+
export declare class MissingMetadataDescription extends RuleCommon implements IRuleDefinition {
|
|
5
|
+
constructor();
|
|
6
|
+
protected check(flow: core.Flow, _options: object | undefined, _suppression: Set<string>): core.Violation[];
|
|
7
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "MissingMetadataDescription", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return MissingMetadataDescription;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _internals = /*#__PURE__*/ _interop_require_wildcard(require("../internals/internals"));
|
|
12
|
+
const _RuleCommon = require("../models/RuleCommon");
|
|
13
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
14
|
+
if (typeof WeakMap !== "function") return null;
|
|
15
|
+
var cacheBabelInterop = new WeakMap();
|
|
16
|
+
var cacheNodeInterop = new WeakMap();
|
|
17
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
18
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
19
|
+
})(nodeInterop);
|
|
20
|
+
}
|
|
21
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
22
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
23
|
+
return obj;
|
|
24
|
+
}
|
|
25
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
26
|
+
return {
|
|
27
|
+
default: obj
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
31
|
+
if (cache && cache.has(obj)) {
|
|
32
|
+
return cache.get(obj);
|
|
33
|
+
}
|
|
34
|
+
var newObj = {
|
|
35
|
+
__proto__: null
|
|
36
|
+
};
|
|
37
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
38
|
+
for(var key in obj){
|
|
39
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
40
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
41
|
+
if (desc && (desc.get || desc.set)) {
|
|
42
|
+
Object.defineProperty(newObj, key, desc);
|
|
43
|
+
} else {
|
|
44
|
+
newObj[key] = obj[key];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
newObj.default = obj;
|
|
49
|
+
if (cache) {
|
|
50
|
+
cache.set(obj, newObj);
|
|
51
|
+
}
|
|
52
|
+
return newObj;
|
|
53
|
+
}
|
|
54
|
+
let MissingMetadataDescription = class MissingMetadataDescription extends _RuleCommon.RuleCommon {
|
|
55
|
+
check(flow, _options, _suppression) {
|
|
56
|
+
const violations = [];
|
|
57
|
+
flow.elements.filter((elem)=>{
|
|
58
|
+
if (elem.metaType !== "metadata" && !elem.element["description"] && elem.subtype !== "start") {
|
|
59
|
+
return elem;
|
|
60
|
+
}
|
|
61
|
+
}).forEach((elem)=>{
|
|
62
|
+
return violations.push(new _internals.Violation(elem));
|
|
63
|
+
});
|
|
64
|
+
return violations;
|
|
65
|
+
}
|
|
66
|
+
constructor(){
|
|
67
|
+
super({
|
|
68
|
+
autoFixable: false,
|
|
69
|
+
description: "Every element must have a meaningful description",
|
|
70
|
+
docRefs: [],
|
|
71
|
+
isConfigurable: false,
|
|
72
|
+
label: "Missing Metadata Description",
|
|
73
|
+
name: "MissingMetadataDescription",
|
|
74
|
+
supportedTypes: _internals.FlowType.allTypes()
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
@@ -39,6 +39,7 @@ const _TriggerOrder = require("../rules/TriggerOrder");
|
|
|
39
39
|
const _UnconnectedElement = require("../rules/UnconnectedElement");
|
|
40
40
|
const _UnsafeRunningContext = require("../rules/UnsafeRunningContext");
|
|
41
41
|
const _UnusedVariable = require("../rules/UnusedVariable");
|
|
42
|
+
const _MissingMetadataDescription = require("../rules/MissingMetadataDescription");
|
|
42
43
|
const DefaultRuleStore = {
|
|
43
44
|
ActionCallsInLoop: _ActionCallsInLoop.ActionCallsInLoop,
|
|
44
45
|
APIVersion: _APIVersion.APIVersion,
|
|
@@ -64,4 +65,6 @@ const DefaultRuleStore = {
|
|
|
64
65
|
UnsafeRunningContext: _UnsafeRunningContext.UnsafeRunningContext,
|
|
65
66
|
UnusedVariable: _UnusedVariable.UnusedVariable
|
|
66
67
|
};
|
|
67
|
-
const BetaRuleStore = {
|
|
68
|
+
const BetaRuleStore = {
|
|
69
|
+
MissingMetadataDescription: _MissingMetadataDescription.MissingMetadataDescription
|
|
70
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flow-scanner/lightning-flow-scanner-core",
|
|
3
3
|
"description": "A lightweight engine for Flow metadata in Node.js, and browser environments. Assess and enhance Salesforce Flow automations for best practices, security, governor limits, and performance issues.",
|
|
4
|
-
"version": "6.
|
|
4
|
+
"version": "6.6.0",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"engines": {
|