@flow-scanner/lightning-flow-scanner-core 6.17.2 → 6.18.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 +33 -10
- package/index.d.ts +4 -3
- package/index.js +14 -0
- package/main/config/RuleRegistry.js +2 -0
- package/main/interfaces/IRuleDefinition.d.ts +1 -0
- package/main/interfaces/IRulesConfig.d.ts +33 -0
- package/main/interfaces/IRulesConfig.js +41 -3
- package/main/libs/FixFlows.d.ts +4 -0
- package/main/libs/FixFlows.js +89 -30
- package/main/libs/GetRuleDefinitions.js +30 -0
- package/main/models/Flow.d.ts +1 -0
- package/main/models/Flow.js +28 -2
- package/main/models/FlowGraph.d.ts +11 -0
- package/main/models/FlowGraph.js +18 -7
- package/main/models/RuleCommon.d.ts +1 -1
- package/main/models/RuleInfo.d.ts +1 -2
- package/main/models/RuleInfo.js +0 -1
- package/main/rules/MissingRecordTriggerFilter.js +4 -2
- package/main/rules/MissingStartReference.d.ts +7 -0
- package/main/rules/MissingStartReference.js +76 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
- [Configure Rules](#configure-rules)
|
|
40
40
|
- [Define Exceptions](#define-exceptions)
|
|
41
41
|
- [Exclude Flows](#exclude-flows)
|
|
42
|
-
- [Scan
|
|
42
|
+
- [Scan Options](#scan-options)
|
|
43
43
|
- **[Installation](#installation)**
|
|
44
44
|
- [Distributions](#distributions)
|
|
45
45
|
- [CICD Templates](#cicd-templates)
|
|
@@ -270,6 +270,10 @@ Auto-Layout automatically arranges and aligns Flow elements, keeping the canvas
|
|
|
270
270
|
**Rule ID:** `missing-auto-layout`
|
|
271
271
|
**Class Name:** _[AutoLayout](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/AutoLayout.ts)_
|
|
272
272
|
**Severity:** 🔵 *Note*
|
|
273
|
+
|
|
274
|
+
#### System (subcategory)
|
|
275
|
+
|
|
276
|
+
System rules are a subset of Layout rules that detect structural issues normally prevented by the Flow Builder UI. See [System Rules Documentation](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/docs/system-rules.md) for the full list.
|
|
273
277
|
<!-- END GENERATED_RULES -->
|
|
274
278
|
|
|
275
279
|
---
|
|
@@ -281,20 +285,26 @@ It is recommend to configure and define:
|
|
|
281
285
|
- The severity of violating any specific rule.
|
|
282
286
|
- Expressions used for rules, such as REGEX patterns and comparison operators.
|
|
283
287
|
- Any known exceptions that should be ignored during scanning.
|
|
288
|
+
- (Optional) Implement filters based on a severity **threshold** or **rule categories**.
|
|
289
|
+
|
|
290
|
+
Most distributions automatically load configuration from:
|
|
291
|
+
- `.flow-scanner.yml`
|
|
292
|
+
- `.flow-scanner.json`
|
|
293
|
+
- `package.json` → `"flowScanner"` key
|
|
284
294
|
|
|
285
295
|
```json
|
|
286
296
|
{
|
|
287
297
|
"rules": {
|
|
288
|
-
//
|
|
298
|
+
// rule customizations (severity, expression, enabled, ...)
|
|
289
299
|
},
|
|
290
300
|
"exceptions": {
|
|
291
|
-
//
|
|
292
|
-
}
|
|
301
|
+
// flow → rule → result suppressions
|
|
302
|
+
},
|
|
303
|
+
"threshold": "error", // only consider errors
|
|
304
|
+
"categories": ["problem", "layout"] // only run rules from these categories
|
|
293
305
|
}
|
|
294
306
|
```
|
|
295
307
|
|
|
296
|
-
Most Lightning Flow Scanner distributions automatically resolve configurations from `.flow-scanner.yml`, `.flow-scanner.json`, or `package.json` → `flowScanner`.
|
|
297
|
-
|
|
298
308
|
### Configure Rules
|
|
299
309
|
|
|
300
310
|
By default, all default rules are executed. You can customize individual rules and override the rules to be executed without having to specify every rule. Below is a breakdown of the available attributes of rule configuration:
|
|
@@ -315,7 +325,7 @@ By default, all default rules are executed. You can customize individual rules a
|
|
|
315
325
|
|
|
316
326
|
#### Configure Severity
|
|
317
327
|
|
|
318
|
-
|
|
328
|
+
Available values for severity are `error`, `warning` and `note`. If no severity is provided, a default value is applied. Configure the severity per rule as demonstrated below:
|
|
319
329
|
|
|
320
330
|
```json
|
|
321
331
|
{
|
|
@@ -444,7 +454,19 @@ Exclude specific flows by their unique API names, regardless of their location.
|
|
|
444
454
|
|
|
445
455
|
**Environment compatibility**: works in **all environments** including Node.js and browser/web distributions, as it operates on parsed flow data rather than file system paths.
|
|
446
456
|
|
|
447
|
-
### Scan
|
|
457
|
+
### Scan Options
|
|
458
|
+
|
|
459
|
+
#### Severity Threshold
|
|
460
|
+
Only report on violations at or above a chosen severity level:
|
|
461
|
+
```json
|
|
462
|
+
{ "threshold": "error" }
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
#### Filter by category
|
|
466
|
+
Restrict the scan to specific categories of rules:
|
|
467
|
+
```json
|
|
468
|
+
{ "categories": ["problem", "layout"] }
|
|
469
|
+
```
|
|
448
470
|
|
|
449
471
|
#### Beta Mode
|
|
450
472
|
|
|
@@ -469,9 +491,10 @@ By default, Lightning Flow Scanner runs **all** default rules and merges any cus
|
|
|
469
491
|
|----------------------------------------------------------------|-----------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
|
470
492
|
| **[Salesforce CLI Plugin](https://www.npmjs.com/package/lightning-flow-scanner)** | Local development, scratch orgs, CI/CD | `sf plugins install lightning-flow-scanner` |
|
|
471
493
|
| **[VS Code Extension](https://open-vsx.org/extension/ForceConfigControl/lightning-flow-scanner-vsx)** | Real-time scanning inside VS Code | `code --install-extension ForceConfigControl.lightning-flow-scanner-vsx` |
|
|
472
|
-
| **[Salesforce App
|
|
494
|
+
| **[Salesforce App](https://github.com/Flow-Scanner/lightning-flow-scanner-app)** | Run scans directly inside a Salesforce org | `sf package install --package 04tgK0000008CLlQAM` |
|
|
473
495
|
| **[GitHub Action](https://github.com/marketplace/actions/lightning-flow-scan)** | Native PR checks | `uses: Flow-Scanner/lightning-flow-scanner@main` |
|
|
474
496
|
| **[Core Library](https://www.npmjs.com/package/@flow-scanner/lightning-flow-scanner-core)** (Node.js + Browser) | Custom tools, scripts, extensions, web apps | `npm install -g @flow-scanner/lightning-flow-scanner-core` |
|
|
497
|
+
| **[Regex Scanner](https://www.npmjs.com/package/@flow-scanner/regex-scanner)** | Regex-based scanning | `npm install -g @flow-scanner/regex-scanner`
|
|
475
498
|
|
|
476
499
|
**Privacy:** Zero user data collected. All processing is client-side. → See our [Security Policy](https://github.com/Flow-Scanner/lightning-flow-scanner?tab=security-ov-file).
|
|
477
500
|
|
|
@@ -628,7 +651,7 @@ For more on Programmatic API, types, and advanced usage of `@flow-scanner/lightn
|
|
|
628
651
|
6. Deploy Demo Flows (Optional):
|
|
629
652
|
|
|
630
653
|
```bash
|
|
631
|
-
|
|
654
|
+
sf project deploy start
|
|
632
655
|
```
|
|
633
656
|
|
|
634
657
|
Navigate to the [Demo Readme](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/example-flows/README.md) for full details
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IRuleDefinition } from "./main/interfaces/IRuleDefinition";
|
|
2
|
-
import type { IRulesConfig } from "./main/interfaces/IRulesConfig";
|
|
2
|
+
import type { IRulesConfig, RuleCategory, Severity, Threshold } from "./main/interfaces/IRulesConfig";
|
|
3
|
+
import { SEVERITY_ORDER, meetsThreshold, countThresholdViolations, filterByThreshold } from "./main/interfaces/IRulesConfig";
|
|
3
4
|
import type { FlatViolation } from "./main/models/FlatViolation";
|
|
4
5
|
import { Compiler } from "./main/libs/Compiler";
|
|
5
6
|
import { exportDetails } from "./main/libs/ExportDetails";
|
|
@@ -22,5 +23,5 @@ import { Violation } from "./main/models/Violation";
|
|
|
22
23
|
import { DEFAULT_ICONS, ASCII_ICONS, type NodeIconConfig } from "./main/config/NodeIcons";
|
|
23
24
|
import { DEFAULT_VARIABLE_ICONS, ASCII_VARIABLE_ICONS, type VariableIconConfig } from "./main/config/VariableIcons";
|
|
24
25
|
import { exportDiagram, type DiagramOptions } from "./main/libs/ExportDiagram";
|
|
25
|
-
export { Compiler, exportDetails, exportDiagram, exportSarif, fix, Flow, FlowAttribute, FlowElement, FlowNode, FlowResource, FlowType, FlowVariable, getRules, parse, ParsedFlow, Violation, RuleResult, scan, ScanResult, DEFAULT_ICONS, ASCII_ICONS, DEFAULT_VARIABLE_ICONS, ASCII_VARIABLE_ICONS, };
|
|
26
|
-
export type { FlatViolation, IRuleDefinition, IRulesConfig, NodeIconConfig, DiagramOptions, VariableIconConfig };
|
|
26
|
+
export { Compiler, exportDetails, exportDiagram, exportSarif, fix, Flow, FlowAttribute, FlowElement, FlowNode, FlowResource, FlowType, FlowVariable, getRules, parse, ParsedFlow, Violation, RuleResult, scan, ScanResult, DEFAULT_ICONS, ASCII_ICONS, DEFAULT_VARIABLE_ICONS, ASCII_VARIABLE_ICONS, SEVERITY_ORDER, meetsThreshold, countThresholdViolations, filterByThreshold, };
|
|
27
|
+
export type { FlatViolation, IRuleDefinition, IRulesConfig, RuleCategory, Severity, Threshold, NodeIconConfig, DiagramOptions, VariableIconConfig };
|
package/index.js
CHANGED
|
@@ -52,12 +52,19 @@ _export(exports, {
|
|
|
52
52
|
get RuleResult () {
|
|
53
53
|
return _RuleResult.RuleResult;
|
|
54
54
|
},
|
|
55
|
+
get // Threshold utilities
|
|
56
|
+
SEVERITY_ORDER () {
|
|
57
|
+
return _IRulesConfig.SEVERITY_ORDER;
|
|
58
|
+
},
|
|
55
59
|
get ScanResult () {
|
|
56
60
|
return _ScanResult.ScanResult;
|
|
57
61
|
},
|
|
58
62
|
get Violation () {
|
|
59
63
|
return _Violation.Violation;
|
|
60
64
|
},
|
|
65
|
+
get countThresholdViolations () {
|
|
66
|
+
return _IRulesConfig.countThresholdViolations;
|
|
67
|
+
},
|
|
61
68
|
get exportDetails () {
|
|
62
69
|
return _ExportDetails.exportDetails;
|
|
63
70
|
},
|
|
@@ -67,12 +74,18 @@ _export(exports, {
|
|
|
67
74
|
get exportSarif () {
|
|
68
75
|
return _ExportSarif.exportSarif;
|
|
69
76
|
},
|
|
77
|
+
get filterByThreshold () {
|
|
78
|
+
return _IRulesConfig.filterByThreshold;
|
|
79
|
+
},
|
|
70
80
|
get fix () {
|
|
71
81
|
return _FixFlows.fix;
|
|
72
82
|
},
|
|
73
83
|
get getRules () {
|
|
74
84
|
return _GetRuleDefinitions.getRules;
|
|
75
85
|
},
|
|
86
|
+
get meetsThreshold () {
|
|
87
|
+
return _IRulesConfig.meetsThreshold;
|
|
88
|
+
},
|
|
76
89
|
get parse () {
|
|
77
90
|
return _ParseFlows.parse;
|
|
78
91
|
},
|
|
@@ -80,6 +93,7 @@ _export(exports, {
|
|
|
80
93
|
return _ScanFlows.scan;
|
|
81
94
|
}
|
|
82
95
|
});
|
|
96
|
+
const _IRulesConfig = require("./main/interfaces/IRulesConfig");
|
|
83
97
|
const _Compiler = require("./main/libs/Compiler");
|
|
84
98
|
const _ExportDetails = require("./main/libs/ExportDetails");
|
|
85
99
|
const _ExportSarif = require("./main/libs/ExportSarif");
|
|
@@ -34,6 +34,7 @@ const _UnsafeRunningContext = require("../rules/UnsafeRunningContext");
|
|
|
34
34
|
const _UnusedVariable = require("../rules/UnusedVariable");
|
|
35
35
|
const _MissingMetadataDescription = require("../rules/MissingMetadataDescription");
|
|
36
36
|
const _MissingRecordTriggerFilter = require("../rules/MissingRecordTriggerFilter");
|
|
37
|
+
const _MissingStartReference = require("../rules/MissingStartReference");
|
|
37
38
|
const _TransformInsteadOfLoop = require("../rules/TransformInsteadOfLoop");
|
|
38
39
|
const _RecordIdAsString = require("../rules/RecordIdAsString");
|
|
39
40
|
function _define_property(obj, key, value) {
|
|
@@ -201,6 +202,7 @@ registry.register("unsafe-running-context", _UnsafeRunningContext.UnsafeRunningC
|
|
|
201
202
|
registry.register("unused-variable", _UnusedVariable.UnusedVariable, "UnusedVariable");
|
|
202
203
|
registry.register("missing-metadata-description", _MissingMetadataDescription.MissingMetadataDescription, "MissingMetadataDescription", true);
|
|
203
204
|
registry.register("missing-record-trigger-filter", _MissingRecordTriggerFilter.MissingRecordTriggerFilter, "MissingFilterRecordTrigger", true);
|
|
205
|
+
registry.register("missing-start-reference", _MissingStartReference.MissingStartReference, "MissingStartReference", true);
|
|
204
206
|
registry.register("transform-instead-of-loop", _TransformInsteadOfLoop.TransformInsteadOfLoop, "TransformInsteadOfLoop", true);
|
|
205
207
|
registry.register("record-id-as-string", _RecordIdAsString.RecordIdAsString, "RecordIdAsString", true);
|
|
206
208
|
registry.register("hardcoded-secret", _HardcodedSecret.HardcodedSecret, "HardcodedSecret", true);
|
|
@@ -4,12 +4,45 @@ export declare enum DetailLevel {
|
|
|
4
4
|
ENRICHED = "enriched",
|
|
5
5
|
SIMPLE = "simple"
|
|
6
6
|
}
|
|
7
|
+
export type RuleCategory = 'problem' | 'suggestion' | 'layout';
|
|
8
|
+
export type Severity = 'error' | 'warning' | 'note';
|
|
9
|
+
export type Threshold = Severity | 'never';
|
|
10
|
+
/** Severity levels ordered from most to least severe */
|
|
11
|
+
export declare const SEVERITY_ORDER: Severity[];
|
|
7
12
|
export interface IRulesConfig {
|
|
8
13
|
betaMode?: boolean;
|
|
9
14
|
betamode?: boolean;
|
|
15
|
+
systemRules?: boolean;
|
|
16
|
+
categories?: RuleCategory[];
|
|
17
|
+
threshold?: Threshold;
|
|
10
18
|
detailLevel?: 'enriched' | 'simple' | DetailLevel;
|
|
11
19
|
exceptions?: IExceptions;
|
|
12
20
|
rules?: IRuleOptions;
|
|
13
21
|
ruleMode?: "merged" | "isolated";
|
|
14
22
|
ignoreFlows?: string[];
|
|
15
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Check if a severity meets or exceeds the threshold.
|
|
26
|
+
* @param severity - The severity to check
|
|
27
|
+
* @param threshold - The threshold to compare against
|
|
28
|
+
* @returns true if severity >= threshold (more severe or equal)
|
|
29
|
+
*/
|
|
30
|
+
export declare function meetsThreshold(severity: string | undefined, threshold: Threshold): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Count violations that meet or exceed the threshold.
|
|
33
|
+
* @param results - Array of results with severity property
|
|
34
|
+
* @param threshold - The threshold to compare against
|
|
35
|
+
* @returns Number of violations meeting the threshold
|
|
36
|
+
*/
|
|
37
|
+
export declare function countThresholdViolations(results: Array<{
|
|
38
|
+
severity?: string;
|
|
39
|
+
}>, threshold: Threshold): number;
|
|
40
|
+
/**
|
|
41
|
+
* Filter results to only include those meeting the threshold.
|
|
42
|
+
* @param results - Array of results with severity property
|
|
43
|
+
* @param threshold - The threshold to filter by
|
|
44
|
+
* @returns Filtered array of results meeting the threshold ('never' returns all)
|
|
45
|
+
*/
|
|
46
|
+
export declare function filterByThreshold<T extends {
|
|
47
|
+
severity?: string;
|
|
48
|
+
}>(results: T[], threshold: Threshold): T[];
|
|
@@ -2,10 +2,27 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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 DetailLevel () {
|
|
8
13
|
return DetailLevel;
|
|
14
|
+
},
|
|
15
|
+
get SEVERITY_ORDER () {
|
|
16
|
+
return SEVERITY_ORDER;
|
|
17
|
+
},
|
|
18
|
+
get countThresholdViolations () {
|
|
19
|
+
return countThresholdViolations;
|
|
20
|
+
},
|
|
21
|
+
get filterByThreshold () {
|
|
22
|
+
return filterByThreshold;
|
|
23
|
+
},
|
|
24
|
+
get meetsThreshold () {
|
|
25
|
+
return meetsThreshold;
|
|
9
26
|
}
|
|
10
27
|
});
|
|
11
28
|
var DetailLevel = /*#__PURE__*/ function(DetailLevel) {
|
|
@@ -13,3 +30,24 @@ var DetailLevel = /*#__PURE__*/ function(DetailLevel) {
|
|
|
13
30
|
DetailLevel["SIMPLE"] = "simple";
|
|
14
31
|
return DetailLevel;
|
|
15
32
|
}({});
|
|
33
|
+
const SEVERITY_ORDER = [
|
|
34
|
+
'error',
|
|
35
|
+
'warning',
|
|
36
|
+
'note'
|
|
37
|
+
];
|
|
38
|
+
function meetsThreshold(severity, threshold) {
|
|
39
|
+
if (threshold === 'never') return false;
|
|
40
|
+
const sev = severity || 'warning';
|
|
41
|
+
const sevIndex = SEVERITY_ORDER.indexOf(sev);
|
|
42
|
+
const thresholdIndex = SEVERITY_ORDER.indexOf(threshold);
|
|
43
|
+
// Lower index = more severe, so severity meets threshold if sevIndex <= thresholdIndex
|
|
44
|
+
return sevIndex >= 0 && sevIndex <= thresholdIndex;
|
|
45
|
+
}
|
|
46
|
+
function countThresholdViolations(results, threshold) {
|
|
47
|
+
if (threshold === 'never') return 0;
|
|
48
|
+
return results.filter((r)=>meetsThreshold(r.severity, threshold)).length;
|
|
49
|
+
}
|
|
50
|
+
function filterByThreshold(results, threshold) {
|
|
51
|
+
if (threshold === 'never') return results;
|
|
52
|
+
return results.filter((r)=>meetsThreshold(r.severity, threshold));
|
|
53
|
+
}
|
package/main/libs/FixFlows.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import * as core from "../internals/internals";
|
|
2
2
|
export declare function fix(results: core.ScanResult[]): core.ScanResult[];
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Use fix() instead which modifies flows in place.
|
|
5
|
+
* Kept for backward compatibility.
|
|
6
|
+
*/
|
|
3
7
|
export declare function FixFlows(flow: core.Flow, ruleResults: core.RuleResult[]): core.Flow;
|
package/main/libs/FixFlows.js
CHANGED
|
@@ -17,7 +17,6 @@ _export(exports, {
|
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
const _internals = /*#__PURE__*/ _interop_require_wildcard(require("../internals/internals"));
|
|
20
|
-
const _BuildFlow = require("./BuildFlow");
|
|
21
20
|
function _getRequireWildcardCache(nodeInterop) {
|
|
22
21
|
if (typeof WeakMap !== "function") return null;
|
|
23
22
|
var cacheBabelInterop = new WeakMap();
|
|
@@ -63,45 +62,105 @@ function fix(results) {
|
|
|
63
62
|
const newResults = [];
|
|
64
63
|
for (const result of results){
|
|
65
64
|
if (!result.ruleResults || result.ruleResults.length === 0) continue;
|
|
66
|
-
const fixables = result.ruleResults.filter((r)=>r.ruleName === "UnusedVariable" && r.occurs || r.ruleName === "UnconnectedElement" && r.occurs);
|
|
65
|
+
const fixables = result.ruleResults.filter((r)=>r.ruleName === "UnusedVariable" && r.occurs || r.ruleName === "UnconnectedElement" && r.occurs || r.ruleName === "AutoLayout" && r.occurs);
|
|
67
66
|
if (fixables.length === 0) continue;
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
71
|
-
result.flow
|
|
72
|
-
newResults.push(result);
|
|
67
|
+
// Handle AutoLayout fix separately (modifies metadata, not elements)
|
|
68
|
+
const autoLayoutFix = fixables.find((r)=>r.ruleName === "AutoLayout");
|
|
69
|
+
if (autoLayoutFix) {
|
|
70
|
+
applyAutoLayoutFix(result.flow);
|
|
73
71
|
}
|
|
72
|
+
// Handle element-based fixes (UnusedVariable, UnconnectedElement)
|
|
73
|
+
// These modify xmldata in place to preserve element order and formatting
|
|
74
|
+
const elementFixables = fixables.filter((r)=>r.ruleName !== "AutoLayout");
|
|
75
|
+
if (elementFixables.length > 0) {
|
|
76
|
+
applyElementFixes(result.flow, elementFixables);
|
|
77
|
+
}
|
|
78
|
+
newResults.push(result);
|
|
74
79
|
}
|
|
75
80
|
return newResults;
|
|
76
81
|
}
|
|
77
|
-
function
|
|
78
|
-
|
|
82
|
+
function applyAutoLayoutFix(flow) {
|
|
83
|
+
if (!flow.xmldata) return;
|
|
84
|
+
// Ensure processMetadataValues is an array
|
|
85
|
+
if (!flow.xmldata.processMetadataValues) {
|
|
86
|
+
flow.xmldata.processMetadataValues = [];
|
|
87
|
+
} else if (!Array.isArray(flow.xmldata.processMetadataValues)) {
|
|
88
|
+
flow.xmldata.processMetadataValues = [
|
|
89
|
+
flow.xmldata.processMetadataValues
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
// Find existing CanvasMode entry
|
|
93
|
+
const canvasModeIndex = flow.xmldata.processMetadataValues.findIndex((mdv)=>mdv.name === "CanvasMode");
|
|
94
|
+
const autoLayoutValue = {
|
|
95
|
+
name: "CanvasMode",
|
|
96
|
+
value: {
|
|
97
|
+
stringValue: "AUTO_LAYOUT_CANVAS"
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
if (canvasModeIndex >= 0) {
|
|
101
|
+
// Update existing entry
|
|
102
|
+
flow.xmldata.processMetadataValues[canvasModeIndex] = autoLayoutValue;
|
|
103
|
+
} else {
|
|
104
|
+
// Add new entry
|
|
105
|
+
flow.xmldata.processMetadataValues.push(autoLayoutValue);
|
|
106
|
+
}
|
|
107
|
+
// Update the flow's processMetadataValues property
|
|
108
|
+
flow.processMetadataValues = flow.xmldata.processMetadataValues;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Apply element-based fixes (UnusedVariable, UnconnectedElement) by modifying xmldata in place.
|
|
112
|
+
* This preserves element order and formatting from the original file.
|
|
113
|
+
*/ function applyElementFixes(flow, ruleResults) {
|
|
114
|
+
var _unusedVariableRes_details, _unconnectedElementsRes_details;
|
|
115
|
+
if (!flow.xmldata) return;
|
|
79
116
|
const unusedVariableRes = ruleResults.find((r)=>r.ruleName === "UnusedVariable");
|
|
80
117
|
var _unusedVariableRes_details_map;
|
|
81
118
|
const unusedVariableNames = new Set((_unusedVariableRes_details_map = unusedVariableRes === null || unusedVariableRes === void 0 ? void 0 : (_unusedVariableRes_details = unusedVariableRes.details) === null || _unusedVariableRes_details === void 0 ? void 0 : _unusedVariableRes_details.map((d)=>d.name)) !== null && _unusedVariableRes_details_map !== void 0 ? _unusedVariableRes_details_map : []);
|
|
82
119
|
const unconnectedElementsRes = ruleResults.find((r)=>r.ruleName === "UnconnectedElement");
|
|
83
120
|
var _unconnectedElementsRes_details_map;
|
|
84
121
|
const unconnectedElementNames = new Set((_unconnectedElementsRes_details_map = unconnectedElementsRes === null || unconnectedElementsRes === void 0 ? void 0 : (_unconnectedElementsRes_details = unconnectedElementsRes.details) === null || _unconnectedElementsRes_details === void 0 ? void 0 : _unconnectedElementsRes_details.map((d)=>d.name)) !== null && _unconnectedElementsRes_details_map !== void 0 ? _unconnectedElementsRes_details_map : []);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
122
|
+
// Remove unused variables from xmldata
|
|
123
|
+
if (unusedVariableNames.size > 0) {
|
|
124
|
+
for (const varTag of _internals.Flow.VARIABLE_TAGS){
|
|
125
|
+
removeElementsByName(flow.xmldata, varTag, unusedVariableNames);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Remove unconnected elements from xmldata
|
|
129
|
+
if (unconnectedElementNames.size > 0) {
|
|
130
|
+
for (const nodeTag of _internals.Flow.NODE_TAGS){
|
|
131
|
+
removeElementsByName(flow.xmldata, nodeTag, unconnectedElementNames);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Update the flow's elements array to match the modified xmldata
|
|
135
|
+
flow.preProcessNodes();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Remove elements from xmldata by name.
|
|
139
|
+
* Handles both single element and array cases.
|
|
140
|
+
*/ function removeElementsByName(xmldata, tagName, namesToRemove) {
|
|
141
|
+
const elements = xmldata[tagName];
|
|
142
|
+
if (!elements) return;
|
|
143
|
+
if (Array.isArray(elements)) {
|
|
144
|
+
const filtered = elements.filter((el)=>!namesToRemove.has(el === null || el === void 0 ? void 0 : el.name));
|
|
145
|
+
if (filtered.length === 0) {
|
|
146
|
+
delete xmldata[tagName];
|
|
147
|
+
} else if (filtered.length === 1) {
|
|
148
|
+
// Keep as single element if only one remains (matches original format)
|
|
149
|
+
xmldata[tagName] = filtered[0];
|
|
150
|
+
} else {
|
|
151
|
+
xmldata[tagName] = filtered;
|
|
103
152
|
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
153
|
+
} else if (typeof elements === 'object' && elements !== null) {
|
|
154
|
+
// Single element case
|
|
155
|
+
if (namesToRemove.has(elements.name)) {
|
|
156
|
+
delete xmldata[tagName];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function FixFlows(flow, ruleResults) {
|
|
161
|
+
// Create a shallow clone of xmldata to avoid modifying the original
|
|
162
|
+
const clonedXmldata = JSON.parse(JSON.stringify(flow.xmldata));
|
|
163
|
+
const clonedFlow = new _internals.Flow(flow.fsPath, clonedXmldata);
|
|
164
|
+
applyElementFixes(clonedFlow, ruleResults);
|
|
165
|
+
return clonedFlow;
|
|
107
166
|
}
|
|
@@ -19,6 +19,8 @@ _export(exports, {
|
|
|
19
19
|
const _RuleRegistry = require("../config/RuleRegistry");
|
|
20
20
|
function GetRuleDefinitions(ruleConfig, options) {
|
|
21
21
|
const includeBeta = (options === null || options === void 0 ? void 0 : options.betaMode) === true || (options === null || options === void 0 ? void 0 : options.betamode) === true;
|
|
22
|
+
const includeSystem = (options === null || options === void 0 ? void 0 : options.systemRules) !== false; // defaults to true
|
|
23
|
+
const categories = options === null || options === void 0 ? void 0 : options.categories; // undefined means all categories
|
|
22
24
|
const rulesMode = (options === null || options === void 0 ? void 0 : options.ruleMode) || "merged";
|
|
23
25
|
const selectedRules = [];
|
|
24
26
|
const ruleIds = _RuleRegistry.ruleRegistry.getAllRuleIds(includeBeta);
|
|
@@ -31,6 +33,10 @@ function GetRuleDefinitions(ruleConfig, options) {
|
|
|
31
33
|
const config = ruleConfig.get(key);
|
|
32
34
|
if ((config === null || config === void 0 ? void 0 : config.enabled) === false) continue;
|
|
33
35
|
const rule = _RuleRegistry.ruleRegistry.createInstance(entry.ruleId); // Always use ruleId to instantiate
|
|
36
|
+
// Skip system rules if disabled
|
|
37
|
+
if (rule.category === 'system' && !includeSystem) continue;
|
|
38
|
+
// Skip rules not in selected categories (if categories filter is specified)
|
|
39
|
+
if (!isCategoryIncluded(rule.category, categories, includeSystem)) continue;
|
|
34
40
|
if (config === null || config === void 0 ? void 0 : config.severity) {
|
|
35
41
|
rule.severity = config.severity;
|
|
36
42
|
}
|
|
@@ -41,6 +47,10 @@ function GetRuleDefinitions(ruleConfig, options) {
|
|
|
41
47
|
// MERGED MODE (default)
|
|
42
48
|
for (const ruleId of ruleIds){
|
|
43
49
|
const rule = _RuleRegistry.ruleRegistry.createInstance(ruleId);
|
|
50
|
+
// Skip system rules if disabled
|
|
51
|
+
if (rule.category === 'system' && !includeSystem) continue;
|
|
52
|
+
// Skip rules not in selected categories (if categories filter is specified)
|
|
53
|
+
if (!isCategoryIncluded(rule.category, categories, includeSystem)) continue;
|
|
44
54
|
var _ruleConfig_get;
|
|
45
55
|
// Try to find config by ruleId first, then fall back to legacy name
|
|
46
56
|
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")
|
|
@@ -53,6 +63,26 @@ function GetRuleDefinitions(ruleConfig, options) {
|
|
|
53
63
|
}
|
|
54
64
|
return selectedRules;
|
|
55
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if a rule's category should be included based on the categories filter.
|
|
68
|
+
* - If no categories filter is specified, all categories are included
|
|
69
|
+
* - System rules are handled separately via includeSystem flag
|
|
70
|
+
* - Rules with matching category are included
|
|
71
|
+
* - Category matching is case-insensitive
|
|
72
|
+
*/ function isCategoryIncluded(ruleCategory, categories, includeSystem) {
|
|
73
|
+
// System category is controlled by systemRules flag, not categories filter
|
|
74
|
+
if (ruleCategory === 'system') {
|
|
75
|
+
return includeSystem;
|
|
76
|
+
}
|
|
77
|
+
// If no categories filter specified, include all non-system categories
|
|
78
|
+
if (!categories || categories.length === 0) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
// Normalize categories to lowercase for case-insensitive matching
|
|
82
|
+
const normalizedCategories = categories.map((c)=>c.toLowerCase());
|
|
83
|
+
// Check if rule's category is in the allowed list (case-insensitive)
|
|
84
|
+
return normalizedCategories.includes(ruleCategory === null || ruleCategory === void 0 ? void 0 : ruleCategory.toLowerCase());
|
|
85
|
+
}
|
|
56
86
|
function getRules(ruleNames, options) {
|
|
57
87
|
return _RuleRegistry.ruleRegistry.getRulesByNames(ruleNames, options);
|
|
58
88
|
}
|
package/main/models/Flow.d.ts
CHANGED
package/main/models/Flow.js
CHANGED
|
@@ -259,16 +259,42 @@ let Flow = class Flow {
|
|
|
259
259
|
};
|
|
260
260
|
const builder = new _fastxmlparser.XMLBuilder(builderOptions);
|
|
261
261
|
const xmldataWithNs = _object_spread({}, this.xmldata);
|
|
262
|
+
// Always ensure the base xmlns is present
|
|
262
263
|
if (!xmldataWithNs["@_xmlns"]) {
|
|
263
264
|
xmldataWithNs["@_xmlns"] = flowXmlNamespace;
|
|
264
265
|
}
|
|
265
|
-
|
|
266
|
+
// Only add xmlns:xsi if the content actually uses xsi: attributes
|
|
267
|
+
// Don't add it unconditionally to avoid unnecessary diffs
|
|
268
|
+
if (!xmldataWithNs["@_xmlns:xsi"] && this.hasXsiAttributes(xmldataWithNs)) {
|
|
266
269
|
xmldataWithNs["@_xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance";
|
|
267
270
|
}
|
|
268
271
|
const rootObj = {
|
|
269
272
|
Flow: xmldataWithNs
|
|
270
273
|
};
|
|
271
|
-
|
|
274
|
+
const xmlContent = builder.build(rootObj);
|
|
275
|
+
// Add XML declaration if not present
|
|
276
|
+
const xmlDeclaration = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
277
|
+
if (!xmlContent.startsWith('<?xml')) {
|
|
278
|
+
return xmlDeclaration + xmlContent;
|
|
279
|
+
}
|
|
280
|
+
return xmlContent;
|
|
281
|
+
}
|
|
282
|
+
hasXsiAttributes(obj) {
|
|
283
|
+
if (obj === null || obj === undefined) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
if (typeof obj !== 'object') {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
for (const key of Object.keys(obj)){
|
|
290
|
+
if (key.includes(':xsi') || key.includes('xsi:')) {
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
if (this.hasXsiAttributes(obj[key])) {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return false;
|
|
272
298
|
}
|
|
273
299
|
constructor(path, data){
|
|
274
300
|
// Flow elements (excludes legacy start nodes)
|
|
@@ -8,6 +8,7 @@ import { FlowNode } from "./FlowNode";
|
|
|
8
8
|
export declare class FlowGraph {
|
|
9
9
|
private nodeMap;
|
|
10
10
|
private reachableFromStart;
|
|
11
|
+
private normalReachableFromStart;
|
|
11
12
|
private elementsInLoop;
|
|
12
13
|
private faultConnectors;
|
|
13
14
|
private normalConnectors;
|
|
@@ -35,6 +36,11 @@ export declare class FlowGraph {
|
|
|
35
36
|
* This reuses the existing IDDFS traversal logic!
|
|
36
37
|
*/
|
|
37
38
|
private computeReachability;
|
|
39
|
+
/**
|
|
40
|
+
* Compute which elements are reachable from start using ONLY normal connectors (not fault connectors).
|
|
41
|
+
* Elements that are reachable overall but NOT reachable via normal connectors are part of fault handling.
|
|
42
|
+
*/
|
|
43
|
+
private computeNormalReachability;
|
|
38
44
|
/**
|
|
39
45
|
* Use Compiler to compute which elements are inside loops.
|
|
40
46
|
* Calls Compiler.traverseFlow() for each loop with endElementName.
|
|
@@ -51,6 +57,11 @@ export declare class FlowGraph {
|
|
|
51
57
|
getAllNextElements(elementName: string): string[];
|
|
52
58
|
getPreviousElements(elementName: string): string[];
|
|
53
59
|
getNode(elementName: string): FlowNode | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Check if an element is part of fault handling flow.
|
|
62
|
+
* An element is part of fault handling if it's only reachable through fault paths
|
|
63
|
+
* (i.e., reachable overall but NOT reachable via normal connectors from START).
|
|
64
|
+
*/
|
|
54
65
|
isPartOfFaultHandling(elementName: string): boolean;
|
|
55
66
|
getLoopNodes(): FlowNode[];
|
|
56
67
|
forEachReachable(callback: (node: FlowNode) => void): void;
|
package/main/models/FlowGraph.js
CHANGED
|
@@ -110,6 +110,15 @@ let FlowGraph = class FlowGraph {
|
|
|
110
110
|
}, this.nodeMap, this.allConnectors);
|
|
111
111
|
}
|
|
112
112
|
/**
|
|
113
|
+
* Compute which elements are reachable from start using ONLY normal connectors (not fault connectors).
|
|
114
|
+
* Elements that are reachable overall but NOT reachable via normal connectors are part of fault handling.
|
|
115
|
+
*/ computeNormalReachability(startReference) {
|
|
116
|
+
const compiler = new _Compiler.Compiler();
|
|
117
|
+
compiler.traverseFlow(startReference, (element)=>{
|
|
118
|
+
this.normalReachableFromStart.add(element.name);
|
|
119
|
+
}, this.nodeMap, this.normalConnectors);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
113
122
|
* Use Compiler to compute which elements are inside loops.
|
|
114
123
|
* Calls Compiler.traverseFlow() for each loop with endElementName.
|
|
115
124
|
*/ computeLoopBoundaries() {
|
|
@@ -167,13 +176,13 @@ let FlowGraph = class FlowGraph {
|
|
|
167
176
|
getNode(elementName) {
|
|
168
177
|
return this.nodeMap.get(elementName);
|
|
169
178
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Check if an element is part of fault handling flow.
|
|
181
|
+
* An element is part of fault handling if it's only reachable through fault paths
|
|
182
|
+
* (i.e., reachable overall but NOT reachable via normal connectors from START).
|
|
183
|
+
*/ isPartOfFaultHandling(elementName) {
|
|
184
|
+
// Element is part of fault handling if it's reachable but NOT reachable via normal paths
|
|
185
|
+
return this.reachableFromStart.has(elementName) && !this.normalReachableFromStart.has(elementName);
|
|
177
186
|
}
|
|
178
187
|
getLoopNodes() {
|
|
179
188
|
return Array.from(this.nodeMap.values()).filter((n)=>n.subtype === "loops");
|
|
@@ -508,6 +517,7 @@ let FlowGraph = class FlowGraph {
|
|
|
508
517
|
_define_property(this, "nodeMap", new Map());
|
|
509
518
|
// Pre-computed sets for common queries (built using Compiler)
|
|
510
519
|
_define_property(this, "reachableFromStart", new Set());
|
|
520
|
+
_define_property(this, "normalReachableFromStart", new Set()); // Elements reachable via normal (non-fault) connectors only
|
|
511
521
|
_define_property(this, "elementsInLoop", new Map()); // element -> loop name
|
|
512
522
|
// Connector metadata (extracted during node processing)
|
|
513
523
|
_define_property(this, "faultConnectors", new Map());
|
|
@@ -527,6 +537,7 @@ let FlowGraph = class FlowGraph {
|
|
|
527
537
|
this.computeLoopBoundaries();
|
|
528
538
|
if (startReference) {
|
|
529
539
|
this.computeReachability(startReference);
|
|
540
|
+
this.computeNormalReachability(startReference);
|
|
530
541
|
}
|
|
531
542
|
}
|
|
532
543
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RuleInfo } from "./RuleInfo";
|
|
2
2
|
import * as core from "../internals/internals";
|
|
3
3
|
export declare abstract class RuleCommon {
|
|
4
|
-
category?: 'problem' | 'suggestion' | 'layout';
|
|
4
|
+
category?: 'problem' | 'suggestion' | 'layout' | 'system';
|
|
5
5
|
description: string;
|
|
6
6
|
summary: string;
|
|
7
7
|
docRefs: Array<{
|
|
@@ -29,9 +29,8 @@ export declare class RuleInfo {
|
|
|
29
29
|
label: string;
|
|
30
30
|
/**
|
|
31
31
|
* The category for the rule.
|
|
32
|
-
* 'problem' | 'suggestion' | 'layout'
|
|
33
32
|
*/
|
|
34
|
-
category: 'problem' | 'suggestion' | 'layout';
|
|
33
|
+
category: 'problem' | 'suggestion' | 'layout' | 'system';
|
|
35
34
|
/**
|
|
36
35
|
* Stable public identifier used for config, suppression, and reporting.
|
|
37
36
|
*/
|
package/main/models/RuleInfo.js
CHANGED
|
@@ -38,7 +38,6 @@ let RuleInfo = class RuleInfo {
|
|
|
38
38
|
*/ _define_property(this, "label", void 0);
|
|
39
39
|
/**
|
|
40
40
|
* The category for the rule.
|
|
41
|
-
* 'problem' | 'suggestion' | 'layout'
|
|
42
41
|
*/ _define_property(this, "category", void 0);
|
|
43
42
|
/**
|
|
44
43
|
* Stable public identifier used for config, suppression, and reporting.
|
|
@@ -66,11 +66,13 @@ let MissingRecordTriggerFilter = class MissingRecordTriggerFilter extends _RuleC
|
|
|
66
66
|
}
|
|
67
67
|
// Check if the flow has filters or entry conditions at the flow level
|
|
68
68
|
const filters = this.getStartProperty(flow, 'filters');
|
|
69
|
+
const filterFormula = this.getStartProperty(flow, 'filterFormula');
|
|
69
70
|
const hasFilters = !!filters;
|
|
71
|
+
const hasFilterFormula = !!filterFormula;
|
|
70
72
|
const scheduledPaths = (_flow_xmldata = flow.xmldata) === null || _flow_xmldata === void 0 ? void 0 : (_flow_xmldata_start = _flow_xmldata.start) === null || _flow_xmldata_start === void 0 ? void 0 : _flow_xmldata_start.scheduledPaths;
|
|
71
73
|
const hasScheduledPaths = !!scheduledPaths;
|
|
72
|
-
// If no filters or scheduled paths (which have their own conditions), flag as violation
|
|
73
|
-
if (!hasFilters && !hasScheduledPaths) {
|
|
74
|
+
// If no filters, formula conditions, or scheduled paths (which have their own conditions), flag as violation
|
|
75
|
+
if (!hasFilters && !hasFilterFormula && !hasScheduledPaths) {
|
|
74
76
|
violations.push(new _internals.Violation(new _internals.FlowAttribute(triggerType, "triggerType", "autolaunched && triggerType")));
|
|
75
77
|
}
|
|
76
78
|
return violations;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as core from "../internals/internals";
|
|
2
|
+
import { RuleCommon } from "../models/RuleCommon";
|
|
3
|
+
import { IRuleDefinition } from "../internals/internals";
|
|
4
|
+
export declare class MissingStartReference extends RuleCommon implements IRuleDefinition {
|
|
5
|
+
constructor();
|
|
6
|
+
protected check(flow: core.Flow, _options: object | undefined, _suppressions: Set<string>): core.Violation[];
|
|
7
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "MissingStartReference", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return MissingStartReference;
|
|
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 MissingStartReference = class MissingStartReference extends _RuleCommon.RuleCommon {
|
|
55
|
+
check(flow, _options, _suppressions) {
|
|
56
|
+
const violations = [];
|
|
57
|
+
if (!flow.startNode) {
|
|
58
|
+
violations.push(new _internals.Violation(new _internals.FlowAttribute("undefined", "startNode", "startNode")));
|
|
59
|
+
}
|
|
60
|
+
return violations;
|
|
61
|
+
}
|
|
62
|
+
constructor(){
|
|
63
|
+
super({
|
|
64
|
+
ruleId: "missing-start-reference",
|
|
65
|
+
category: "system",
|
|
66
|
+
name: "MissingStartReference",
|
|
67
|
+
label: "Missing Start Reference",
|
|
68
|
+
description: "When a flow has no start reference.",
|
|
69
|
+
summary: "Ensure flow has a start reference node",
|
|
70
|
+
supportedTypes: _internals.FlowType.allTypes(),
|
|
71
|
+
docRefs: []
|
|
72
|
+
}, {
|
|
73
|
+
severity: "error"
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
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.18.0",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|