@flow-scanner/lightning-flow-scanner-core 6.10.5 → 6.11.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 +19 -16
- package/index.d.ts +5 -2
- package/index.js +19 -0
- package/main/config/NodeIcons.d.ts +24 -0
- package/main/config/NodeIcons.js +122 -0
- package/main/config/VariableIcons.d.ts +25 -0
- package/main/config/VariableIcons.js +53 -0
- package/main/libs/Compiler.d.ts +1 -2
- package/main/libs/Compiler.js +10 -16
- package/main/libs/ExportDiagram.d.ts +41 -0
- package/main/libs/ExportDiagram.js +40 -0
- package/main/libs/ExportSarif.js +1 -1
- package/main/models/Flow.d.ts +42 -11
- package/main/models/Flow.js +164 -76
- package/main/models/FlowGraph.d.ts +85 -0
- package/main/models/FlowGraph.js +532 -0
- package/main/models/FlowNode.d.ts +58 -2
- package/main/models/FlowNode.js +161 -3
- package/main/models/FlowVariable.d.ts +59 -1
- package/main/models/FlowVariable.js +118 -1
- package/main/models/LoopRuleCommon.js +11 -12
- package/main/models/ParsedFlow.d.ts +1 -1
- package/main/models/RuleCommon.d.ts +30 -7
- package/main/models/RuleCommon.js +49 -11
- package/main/rules/APIVersion.js +31 -1
- package/main/rules/DuplicateDMLOperation.d.ts +1 -2
- package/main/rules/DuplicateDMLOperation.js +35 -73
- package/main/rules/MissingFaultPath.d.ts +4 -0
- package/main/rules/MissingFaultPath.js +19 -15
- package/main/rules/MissingFilterRecordTrigger.js +4 -4
- package/main/rules/RecordIdAsString.js +3 -2
- package/main/rules/RecursiveAfterUpdate.js +7 -4
- package/main/rules/SameRecordFieldUpdates.js +5 -3
- package/main/rules/TriggerOrder.d.ts +0 -1
- package/main/rules/TriggerOrder.js +8 -19
- package/main/rules/UnconnectedElement.d.ts +0 -1
- package/main/rules/UnconnectedElement.js +2 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<a href="https://github.com/Flow-Scanner/lightning-flow-scanner/
|
|
3
|
-
<img src="https://img.shields.io/github/
|
|
4
|
-
</a>
|
|
5
|
-
<a href="https://github.com/Flow-Scanner/lightning-flow-scanner/graphs/contributors">
|
|
6
|
-
<img src="https://img.shields.io/github/contributors/Flow-Scanner/lightning-flow-scanner.svg?style=flat-square" alt="Contributors">
|
|
2
|
+
<a href="https://github.com/Flow-Scanner/lightning-flow-scanner/stargazers">
|
|
3
|
+
<img src="https://img.shields.io/github/stars/Flow-Scanner/lightning-flow-scanner?label=Stargazers&style=flat-square" alt="GitHub stars">
|
|
7
4
|
</a>
|
|
8
|
-
|
|
9
|
-
<img src="https://img.shields.io/npm/v/@flow-scanner/lightning-flow-scanner-core?label=
|
|
5
|
+
<a href="https://www.npmjs.com/package/@flow-scanner/lightning-flow-scanner-core">
|
|
6
|
+
<img src="https://img.shields.io/npm/v/@flow-scanner/lightning-flow-scanner-core?label=Core&style=flat-square" alt="Core version">
|
|
10
7
|
</a>
|
|
11
8
|
<a href="https://www.npmjs.com/package/lightning-flow-scanner">
|
|
12
|
-
<img src="https://img.shields.io/npm/v/lightning-flow-scanner?label=
|
|
9
|
+
<img src="https://img.shields.io/npm/v/lightning-flow-scanner?label=CLI&style=flat-square" alt="CLI version">
|
|
13
10
|
</a>
|
|
14
11
|
<a href="https://open-vsx.org/extension/ForceConfigControl/lightning-flow-scanner-vsx">
|
|
15
|
-
<img src="https://img.shields.io/open-vsx/v/ForceConfigControl/lightning-flow-scanner-vsx?label=
|
|
16
|
-
</a>
|
|
17
|
-
<a href="https://github.com/Flow-Scanner/lightning-flow-scanner/stargazers">
|
|
18
|
-
<img src="https://img.shields.io/github/stars/Flow-Scanner/lightning-flow-scanner?style=flat-square" alt="GitHub stars">
|
|
12
|
+
<img src="https://img.shields.io/open-vsx/v/ForceConfigControl/lightning-flow-scanner-vsx?label=VS%20Code&style=flat-square" alt="VS Code version">
|
|
19
13
|
</a>
|
|
20
14
|
<a href="https://www.npmjs.com/package/lightning-flow-scanner-core">
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
<img src="https://img.shields.io/npm/dt/lightning-flow-scanner-core?label=Downloads%3Cv6&style=flat-square" alt="Downloads <v6">
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://www.npmjs.com/package/@flow-scanner/lightning-flow-scanner-core">
|
|
18
|
+
<img src="https://img.shields.io/npm/dt/@flow-scanner/lightning-flow-scanner-core?label=Downloads%3Ev6&style=flat-square" alt="Downloads >v6">
|
|
19
|
+
</a>
|
|
23
20
|
</p>
|
|
24
21
|
|
|
25
22
|
<p align="center">
|
|
@@ -355,9 +352,15 @@ Use `lightning-flow-scanner-core` as a Node.js/browser dependency:
|
|
|
355
352
|
import { parse, scan } from "@flow-scanner/lightning-flow-scanner-core";
|
|
356
353
|
parse("flows/*.xml").then(scan);
|
|
357
354
|
|
|
358
|
-
// Get SARIF output
|
|
355
|
+
// Get SARIF output (e.g. for GitHub Code Scanning)
|
|
359
356
|
import { parse, scan, exportSarif } from "@flow-scanner/lightning-flow-scanner-core";
|
|
360
|
-
parse("flows
|
|
357
|
+
parse("flows/**/*.flow-meta.xml").then(scan).then(exportSarif)
|
|
358
|
+
// .then(sarif => fs.writeFile("results.sarif", sarif))
|
|
359
|
+
|
|
360
|
+
// Generate Markdown documentation with Mermaid flow diagrams
|
|
361
|
+
import { parse, exportDiagram } from "@flow-scanner/lightning-flow-scanner-core";
|
|
362
|
+
parse("flows/**/*.flow-meta.xml").then(exportDiagram)
|
|
363
|
+
// .then(md => fs.writeFile("FLOW_DOCUMENTATION.md", md))
|
|
361
364
|
|
|
362
365
|
// Browser Usage (Tooling API)
|
|
363
366
|
const { Flow, scan } = window.lightningflowscanner;
|
package/index.d.ts
CHANGED
|
@@ -19,5 +19,8 @@ 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
|
-
|
|
23
|
-
|
|
22
|
+
import { DEFAULT_ICONS, ASCII_ICONS, type NodeIconConfig } from "./main/config/NodeIcons";
|
|
23
|
+
import { DEFAULT_VARIABLE_ICONS, ASCII_VARIABLE_ICONS, type VariableIconConfig } from "./main/config/VariableIcons";
|
|
24
|
+
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 };
|
package/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// Updated exports in index.ts
|
|
1
2
|
"use strict";
|
|
2
3
|
Object.defineProperty(exports, "__esModule", {
|
|
3
4
|
value: true
|
|
@@ -9,9 +10,21 @@ function _export(target, all) {
|
|
|
9
10
|
});
|
|
10
11
|
}
|
|
11
12
|
_export(exports, {
|
|
13
|
+
get ASCII_ICONS () {
|
|
14
|
+
return _NodeIcons.ASCII_ICONS;
|
|
15
|
+
},
|
|
16
|
+
get ASCII_VARIABLE_ICONS () {
|
|
17
|
+
return _VariableIcons.ASCII_VARIABLE_ICONS;
|
|
18
|
+
},
|
|
12
19
|
get Compiler () {
|
|
13
20
|
return _Compiler.Compiler;
|
|
14
21
|
},
|
|
22
|
+
get DEFAULT_ICONS () {
|
|
23
|
+
return _NodeIcons.DEFAULT_ICONS;
|
|
24
|
+
},
|
|
25
|
+
get DEFAULT_VARIABLE_ICONS () {
|
|
26
|
+
return _VariableIcons.DEFAULT_VARIABLE_ICONS;
|
|
27
|
+
},
|
|
15
28
|
get Flow () {
|
|
16
29
|
return _Flow.Flow;
|
|
17
30
|
},
|
|
@@ -48,6 +61,9 @@ _export(exports, {
|
|
|
48
61
|
get exportDetails () {
|
|
49
62
|
return _ExportDetails.exportDetails;
|
|
50
63
|
},
|
|
64
|
+
get exportDiagram () {
|
|
65
|
+
return _ExportDiagram.exportDiagram;
|
|
66
|
+
},
|
|
51
67
|
get exportSarif () {
|
|
52
68
|
return _ExportSarif.exportSarif;
|
|
53
69
|
},
|
|
@@ -82,3 +98,6 @@ const _ParsedFlow = require("./main/models/ParsedFlow");
|
|
|
82
98
|
const _RuleResult = require("./main/models/RuleResult");
|
|
83
99
|
const _ScanResult = require("./main/models/ScanResult");
|
|
84
100
|
const _Violation = require("./main/models/Violation");
|
|
101
|
+
const _NodeIcons = require("./main/config/NodeIcons");
|
|
102
|
+
const _VariableIcons = require("./main/config/VariableIcons");
|
|
103
|
+
const _ExportDiagram = require("./main/libs/ExportDiagram");
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon configuration for flow node types.
|
|
3
|
+
* Emoji are safe in UTF-8 encoded source files and will work in all modern builds.
|
|
4
|
+
* If you need ASCII fallback, use FlowNode.setIconConfig(ASCII_ICONS)
|
|
5
|
+
*/
|
|
6
|
+
export interface NodeIconConfig {
|
|
7
|
+
[nodeType: string]: {
|
|
8
|
+
[subtype: string]: string;
|
|
9
|
+
} | {
|
|
10
|
+
default: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Default icons using emoji (recommended for modern environments)
|
|
15
|
+
*/
|
|
16
|
+
export declare const DEFAULT_ICONS: NodeIconConfig;
|
|
17
|
+
/**
|
|
18
|
+
* ASCII fallback icons (for environments without emoji support)
|
|
19
|
+
*/
|
|
20
|
+
export declare const ASCII_ICONS: NodeIconConfig;
|
|
21
|
+
/**
|
|
22
|
+
* Get default icon config (can be overridden at runtime)
|
|
23
|
+
*/
|
|
24
|
+
export declare function getDefaultIconConfig(): NodeIconConfig;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon configuration for flow node types.
|
|
3
|
+
* Emoji are safe in UTF-8 encoded source files and will work in all modern builds.
|
|
4
|
+
* If you need ASCII fallback, use FlowNode.setIconConfig(ASCII_ICONS)
|
|
5
|
+
*/ "use strict";
|
|
6
|
+
Object.defineProperty(exports, "__esModule", {
|
|
7
|
+
value: true
|
|
8
|
+
});
|
|
9
|
+
function _export(target, all) {
|
|
10
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
_export(exports, {
|
|
16
|
+
get ASCII_ICONS () {
|
|
17
|
+
return ASCII_ICONS;
|
|
18
|
+
},
|
|
19
|
+
get DEFAULT_ICONS () {
|
|
20
|
+
return DEFAULT_ICONS;
|
|
21
|
+
},
|
|
22
|
+
get getDefaultIconConfig () {
|
|
23
|
+
return getDefaultIconConfig;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const DEFAULT_ICONS = {
|
|
27
|
+
actionCalls: {
|
|
28
|
+
apex: '\u2699\uFE0F',
|
|
29
|
+
emailAlert: '\uD83D\uDCE7',
|
|
30
|
+
emailSimple: '\uD83D\uDCE7',
|
|
31
|
+
submit: '\u26A1',
|
|
32
|
+
default: '\u26A1' // HIGH VOLTAGE
|
|
33
|
+
},
|
|
34
|
+
assignments: {
|
|
35
|
+
default: '\uD83D\uDFF0' // 🟰 HEAVY EQUALS SIGN
|
|
36
|
+
},
|
|
37
|
+
collectionProcessors: {
|
|
38
|
+
FilterCollectionProcessor: '\uD83D\uDD3D',
|
|
39
|
+
SortCollectionProcessor: '\uD83D\uDD03',
|
|
40
|
+
default: '\uD83D\uDCE6' // PACKAGE
|
|
41
|
+
},
|
|
42
|
+
customErrors: {
|
|
43
|
+
default: '\uD83D\uDEAB' // PROHIBITED
|
|
44
|
+
},
|
|
45
|
+
decisions: {
|
|
46
|
+
default: '\uD83D\uDD00' // TWISTED ARROWS
|
|
47
|
+
},
|
|
48
|
+
loops: {
|
|
49
|
+
default: '\uD83D\uDD01' // REPEAT BUTTON
|
|
50
|
+
},
|
|
51
|
+
recordCreates: {
|
|
52
|
+
default: '\u2795' // PLUS
|
|
53
|
+
},
|
|
54
|
+
recordDeletes: {
|
|
55
|
+
default: '\uD83D\uDDD1\uFE0F' // WASTEBASKET
|
|
56
|
+
},
|
|
57
|
+
recordLookups: {
|
|
58
|
+
default: '\uD83D\uDD0D' // MAGNIFYING GLASS
|
|
59
|
+
},
|
|
60
|
+
recordUpdates: {
|
|
61
|
+
default: '\uD83D\uDEE0\uFE0F' // HAMMER AND WRENCH
|
|
62
|
+
},
|
|
63
|
+
screens: {
|
|
64
|
+
default: '\uD83D\uDCBB' // LAPTOP
|
|
65
|
+
},
|
|
66
|
+
subflows: {
|
|
67
|
+
default: '\uD83D\uDD17' // LINK
|
|
68
|
+
},
|
|
69
|
+
transforms: {
|
|
70
|
+
default: '\u267B\uFE0F'
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const ASCII_ICONS = {
|
|
74
|
+
actionCalls: {
|
|
75
|
+
apex: '[A]',
|
|
76
|
+
emailAlert: '[E]',
|
|
77
|
+
emailSimple: '[E]',
|
|
78
|
+
submit: '[!]',
|
|
79
|
+
default: '[!]'
|
|
80
|
+
},
|
|
81
|
+
assignments: {
|
|
82
|
+
default: '[=]'
|
|
83
|
+
},
|
|
84
|
+
collectionProcessors: {
|
|
85
|
+
FilterCollectionProcessor: '[F]',
|
|
86
|
+
SortCollectionProcessor: '[S]',
|
|
87
|
+
default: '[C]'
|
|
88
|
+
},
|
|
89
|
+
customErrors: {
|
|
90
|
+
default: '[X]'
|
|
91
|
+
},
|
|
92
|
+
decisions: {
|
|
93
|
+
default: '[?]'
|
|
94
|
+
},
|
|
95
|
+
loops: {
|
|
96
|
+
default: '[L]'
|
|
97
|
+
},
|
|
98
|
+
recordCreates: {
|
|
99
|
+
default: '[+]'
|
|
100
|
+
},
|
|
101
|
+
recordDeletes: {
|
|
102
|
+
default: '[-]'
|
|
103
|
+
},
|
|
104
|
+
recordLookups: {
|
|
105
|
+
default: '[S]'
|
|
106
|
+
},
|
|
107
|
+
recordUpdates: {
|
|
108
|
+
default: '[U]'
|
|
109
|
+
},
|
|
110
|
+
screens: {
|
|
111
|
+
default: '[#]'
|
|
112
|
+
},
|
|
113
|
+
subflows: {
|
|
114
|
+
default: '[>]'
|
|
115
|
+
},
|
|
116
|
+
transforms: {
|
|
117
|
+
default: '[T]'
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
function getDefaultIconConfig() {
|
|
121
|
+
return DEFAULT_ICONS;
|
|
122
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon configuration for flow variable types.
|
|
3
|
+
* Includes icons for variable subtypes and boolean states.
|
|
4
|
+
*/
|
|
5
|
+
export interface VariableIconConfig {
|
|
6
|
+
subtypes: {
|
|
7
|
+
[subtype: string]: string;
|
|
8
|
+
};
|
|
9
|
+
boolean: {
|
|
10
|
+
true: string;
|
|
11
|
+
false: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Default icons using emoji (recommended for modern environments)
|
|
16
|
+
*/
|
|
17
|
+
export declare const DEFAULT_VARIABLE_ICONS: VariableIconConfig;
|
|
18
|
+
/**
|
|
19
|
+
* ASCII fallback icons (for environments without emoji support)
|
|
20
|
+
*/
|
|
21
|
+
export declare const ASCII_VARIABLE_ICONS: VariableIconConfig;
|
|
22
|
+
/**
|
|
23
|
+
* Get default variable icon config
|
|
24
|
+
*/
|
|
25
|
+
export declare function getDefaultVariableIconConfig(): VariableIconConfig;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon configuration for flow variable types.
|
|
3
|
+
* Includes icons for variable subtypes and boolean states.
|
|
4
|
+
*/ "use strict";
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
function _export(target, all) {
|
|
9
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
_export(exports, {
|
|
15
|
+
get ASCII_VARIABLE_ICONS () {
|
|
16
|
+
return ASCII_VARIABLE_ICONS;
|
|
17
|
+
},
|
|
18
|
+
get DEFAULT_VARIABLE_ICONS () {
|
|
19
|
+
return DEFAULT_VARIABLE_ICONS;
|
|
20
|
+
},
|
|
21
|
+
get getDefaultVariableIconConfig () {
|
|
22
|
+
return getDefaultVariableIconConfig;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const DEFAULT_VARIABLE_ICONS = {
|
|
26
|
+
subtypes: {
|
|
27
|
+
variables: '\uD83D\uDCCA',
|
|
28
|
+
constants: '\uD83D\uDD12',
|
|
29
|
+
formulas: '\uD83E\uDDEE',
|
|
30
|
+
choices: '\uD83D\uDCCB',
|
|
31
|
+
dynamicChoiceSets: '\uD83D\uDD04'
|
|
32
|
+
},
|
|
33
|
+
boolean: {
|
|
34
|
+
true: '\u2705',
|
|
35
|
+
false: '\u2B1C'
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const ASCII_VARIABLE_ICONS = {
|
|
39
|
+
subtypes: {
|
|
40
|
+
variables: '[V]',
|
|
41
|
+
constants: '[C]',
|
|
42
|
+
formulas: '[F]',
|
|
43
|
+
choices: '[CH]',
|
|
44
|
+
dynamicChoiceSets: '[D]'
|
|
45
|
+
},
|
|
46
|
+
boolean: {
|
|
47
|
+
true: '[X]',
|
|
48
|
+
false: '[ ]'
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
function getDefaultVariableIconConfig() {
|
|
52
|
+
return DEFAULT_VARIABLE_ICONS;
|
|
53
|
+
}
|
package/main/libs/Compiler.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { Flow } from "../models/Flow";
|
|
2
1
|
import { FlowNode } from "../models/FlowNode";
|
|
3
2
|
export declare class Compiler {
|
|
4
3
|
visitedElements: Set<string>;
|
|
5
4
|
constructor();
|
|
6
|
-
traverseFlow(
|
|
5
|
+
traverseFlow(startElementName: string, visitCallback: (element: FlowNode) => void, nodeMap: Map<string, FlowNode>, allConnectors: Map<string, Set<string>>, endElementName?: string): void;
|
|
7
6
|
private findNextElements;
|
|
8
7
|
}
|
package/main/libs/Compiler.js
CHANGED
|
@@ -22,7 +22,7 @@ function _define_property(obj, key, value) {
|
|
|
22
22
|
return obj;
|
|
23
23
|
}
|
|
24
24
|
let Compiler = class Compiler {
|
|
25
|
-
traverseFlow(
|
|
25
|
+
traverseFlow(startElementName, visitCallback, nodeMap, allConnectors, endElementName) {
|
|
26
26
|
// Iterative Deepening Depth-First Search (IDDFS)
|
|
27
27
|
let elementsToVisit = [
|
|
28
28
|
startElementName
|
|
@@ -31,12 +31,11 @@ let Compiler = class Compiler {
|
|
|
31
31
|
const nextElements = [];
|
|
32
32
|
for (const elementName of elementsToVisit){
|
|
33
33
|
if (!this.visitedElements.has(elementName)) {
|
|
34
|
-
|
|
35
|
-
const currentElement = (_flow_elements = flow.elements) === null || _flow_elements === void 0 ? void 0 : _flow_elements.find((element)=>element.name === elementName);
|
|
34
|
+
const currentElement = nodeMap.get(elementName);
|
|
36
35
|
if (currentElement) {
|
|
37
36
|
visitCallback(currentElement);
|
|
38
37
|
this.visitedElements.add(elementName);
|
|
39
|
-
nextElements.push(...this.findNextElements(
|
|
38
|
+
nextElements.push(...this.findNextElements(elementName, allConnectors, nodeMap, endElementName));
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
41
|
}
|
|
@@ -46,19 +45,14 @@ let Compiler = class Compiler {
|
|
|
46
45
|
elementsToVisit = nextElements;
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
|
-
findNextElements(
|
|
48
|
+
findNextElements(elementName, allConnectors, nodeMap, endElementName) {
|
|
50
49
|
const nextElements = [];
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const targetReference = (_connector_connectorTargetReference_targetReference = connector === null || connector === void 0 ? void 0 : (_connector_connectorTargetReference = connector.connectorTargetReference) === null || _connector_connectorTargetReference === void 0 ? void 0 : _connector_connectorTargetReference.targetReference) !== null && _connector_connectorTargetReference_targetReference !== void 0 ? _connector_connectorTargetReference_targetReference : connector.reference;
|
|
58
|
-
// Check if the reference exists in the flow elements
|
|
59
|
-
const nextElement = (_flow_elements = flow.elements) === null || _flow_elements === void 0 ? void 0 : _flow_elements.find((element)=>element.metaType === "node" && element.name === targetReference);
|
|
60
|
-
if (nextElement && nextElement.metaType === "node" && nextElement.name !== endElementName) {
|
|
61
|
-
nextElements.push(nextElement.name);
|
|
50
|
+
const targets = allConnectors.get(elementName);
|
|
51
|
+
if (targets) {
|
|
52
|
+
for (const targetReference of targets){
|
|
53
|
+
if (targetReference !== endElementName && nodeMap.has(targetReference)) {
|
|
54
|
+
nextElements.push(targetReference);
|
|
55
|
+
}
|
|
62
56
|
}
|
|
63
57
|
}
|
|
64
58
|
return nextElements;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ParsedFlow } from "../models/ParsedFlow";
|
|
2
|
+
export interface DiagramOptions {
|
|
3
|
+
includeDetails?: boolean;
|
|
4
|
+
includeMarkdownDocs?: boolean;
|
|
5
|
+
collapsedDetails?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Generate Markdown documentation for parsed flows, including Mermaid diagrams for valid flows.
|
|
9
|
+
*
|
|
10
|
+
* This function can be chained after parse() to generate documentation.
|
|
11
|
+
* It filters out errored parses and only documents valid flows.
|
|
12
|
+
*
|
|
13
|
+
* @param parsedFlows Array of ParsedFlow objects from parse()
|
|
14
|
+
* @param options Visualization options for Mermaid diagrams
|
|
15
|
+
*
|
|
16
|
+
* @returns Markdown string with documentation
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Node.js usage
|
|
21
|
+
* import { parse, exportFlowMarkdown } from "@flow-scanner/lightning-flow-scanner-core";
|
|
22
|
+
* import fs from "fs/promises";
|
|
23
|
+
*
|
|
24
|
+
* const parsed = await parse(["flows/*.xml"]);
|
|
25
|
+
* const md = exportFlowMarkdown(parsed, {
|
|
26
|
+
* includeDetails: true,
|
|
27
|
+
* includeMarkdownDocs: true,
|
|
28
|
+
* collapsedDetails: true
|
|
29
|
+
* });
|
|
30
|
+
* await fs.writeFile("flow-doc.md", md);
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* // Chained (async/await)
|
|
36
|
+
* const md = await parse(["flows/*.xml"]).then(parsed =>
|
|
37
|
+
* exportFlowMarkdown(parsed, { includeDetails: true })
|
|
38
|
+
* );
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function exportDiagram(parsedFlows: ParsedFlow[], options?: DiagramOptions): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "exportDiagram", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return exportDiagram;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
function exportDiagram(parsedFlows, options = {
|
|
12
|
+
includeDetails: true,
|
|
13
|
+
includeMarkdownDocs: true,
|
|
14
|
+
collapsedDetails: true
|
|
15
|
+
}) {
|
|
16
|
+
const validFlows = parsedFlows.filter((p)=>p.flow).map((p)=>p.flow);
|
|
17
|
+
let markdown = "# Flow Documentation\n\n";
|
|
18
|
+
if (validFlows.length === 0) {
|
|
19
|
+
markdown += "No valid flows found.\n\n";
|
|
20
|
+
}
|
|
21
|
+
for (const flow of validFlows){
|
|
22
|
+
markdown += `## ${flow.name}\n\n`;
|
|
23
|
+
const vizOptions = {
|
|
24
|
+
includeDetails: options.includeDetails,
|
|
25
|
+
includeMarkdownDocs: options.includeMarkdownDocs,
|
|
26
|
+
collapsedDetails: options.collapsedDetails
|
|
27
|
+
};
|
|
28
|
+
markdown += flow.visualize("mermaid", vizOptions) + "\n\n";
|
|
29
|
+
}
|
|
30
|
+
// Optionally add errors section if any
|
|
31
|
+
const errors = parsedFlows.filter((p)=>p.errorMessage);
|
|
32
|
+
if (errors.length > 0) {
|
|
33
|
+
markdown += "## Parse Errors\n\n";
|
|
34
|
+
for (const err of errors){
|
|
35
|
+
markdown += `- ${err.uri}: ${err.errorMessage}\n`;
|
|
36
|
+
}
|
|
37
|
+
markdown += "\n";
|
|
38
|
+
}
|
|
39
|
+
return markdown;
|
|
40
|
+
}
|
package/main/libs/ExportSarif.js
CHANGED
|
@@ -74,7 +74,7 @@ function exportSarif(results) {
|
|
|
74
74
|
}))),
|
|
75
75
|
tool: {
|
|
76
76
|
driver: {
|
|
77
|
-
informationUri: "https://github.com/Flow-Scanner/lightning-flow-scanner
|
|
77
|
+
informationUri: "https://github.com/Flow-Scanner/lightning-flow-scanner",
|
|
78
78
|
name: "Lightning Flow Scanner",
|
|
79
79
|
rules: result.ruleResults.filter((r)=>r.occurs).map((r)=>({
|
|
80
80
|
defaultConfiguration: {
|
package/main/models/Flow.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { FlowElement } from "./FlowElement";
|
|
2
|
+
import { FlowNode } from "./FlowNode";
|
|
3
|
+
import { FlowGraph } from "./FlowGraph";
|
|
2
4
|
export declare class Flow {
|
|
3
5
|
/**
|
|
4
6
|
* Metadata Tags of Salesforce Flow Attributes
|
|
@@ -7,32 +9,61 @@ export declare class Flow {
|
|
|
7
9
|
/**
|
|
8
10
|
* Metadata Tags of Salesforce Flow Nodes
|
|
9
11
|
*/
|
|
10
|
-
static readonly NODE_TAGS: readonly ["actionCalls", "apexPluginCalls", "assignments", "collectionProcessors", "decisions", "loops", "orchestratedStages", "recordCreates", "recordDeletes", "recordLookups", "recordUpdates", "recordRollbacks", "screens", "
|
|
12
|
+
static readonly NODE_TAGS: readonly ["actionCalls", "apexPluginCalls", "assignments", "collectionProcessors", "decisions", "loops", "orchestratedStages", "recordCreates", "recordDeletes", "recordLookups", "recordUpdates", "recordRollbacks", "screens", "steps", "subflows", "waits", "transforms", "customErrors"];
|
|
11
13
|
static readonly RESOURCE_TAGS: readonly ["textTemplates", "stages"];
|
|
12
14
|
static readonly VARIABLE_TAGS: readonly ["choices", "constants", "dynamicChoiceSets", "formulas", "variables"];
|
|
13
|
-
elements
|
|
15
|
+
elements: FlowElement[];
|
|
14
16
|
fsPath?: string;
|
|
15
17
|
uri?: string;
|
|
16
|
-
interviewLabel?: string;
|
|
17
18
|
label: string;
|
|
18
|
-
|
|
19
|
+
interviewLabel?: string;
|
|
20
|
+
name: string;
|
|
19
21
|
processMetadataValues?: any;
|
|
20
|
-
processType
|
|
21
|
-
|
|
22
|
+
processType: string;
|
|
23
|
+
type: string;
|
|
24
|
+
status: string;
|
|
25
|
+
triggerOrder?: number;
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated Use startNode.element instead. Kept for backward compatibility.
|
|
28
|
+
*/
|
|
22
29
|
start?: any;
|
|
30
|
+
/**
|
|
31
|
+
* Direct reference to first element (from XML attribute).
|
|
32
|
+
* Used in newer flows as an alternative to the start element.
|
|
33
|
+
*/
|
|
23
34
|
startElementReference?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Computed reference to the first element to execute.
|
|
37
|
+
* This is what rules should use for traversal.
|
|
38
|
+
*/
|
|
24
39
|
startReference?: string;
|
|
25
|
-
status?: string;
|
|
26
|
-
triggerOrder?: number;
|
|
27
|
-
type?: string;
|
|
28
40
|
/**
|
|
29
|
-
*
|
|
41
|
+
* Parsed FlowNode object of the start element.
|
|
42
|
+
* Contains trigger information and connectors.
|
|
43
|
+
* Access start element data via startNode.element
|
|
30
44
|
*/
|
|
45
|
+
startNode?: FlowNode;
|
|
46
|
+
private _graph?;
|
|
47
|
+
get graph(): FlowGraph;
|
|
48
|
+
root?: any;
|
|
31
49
|
xmldata: any;
|
|
32
50
|
constructor(path?: string, data?: unknown);
|
|
33
51
|
static from(obj: Partial<Flow>): Flow;
|
|
34
52
|
preProcessNodes(): void;
|
|
35
|
-
|
|
53
|
+
visualize(format?: 'mermaid' | 'plantuml', options?: {
|
|
54
|
+
includeDetails?: boolean;
|
|
55
|
+
includeMarkdownDocs?: boolean;
|
|
56
|
+
collapsedDetails?: boolean;
|
|
57
|
+
}): string;
|
|
58
|
+
private processNodeType;
|
|
59
|
+
/**
|
|
60
|
+
* Find the name of the first element to execute.
|
|
61
|
+
* Priority order:
|
|
62
|
+
* 1. startElementReference (newer flows, direct XML attribute)
|
|
63
|
+
* 2. Start node connector (older flows, points to first element)
|
|
64
|
+
* 3. Start node scheduledPaths (async flows)
|
|
65
|
+
*/
|
|
36
66
|
private findStart;
|
|
67
|
+
toXMLString(): string;
|
|
37
68
|
private generateDoc;
|
|
38
69
|
}
|