@flow-scanner/lightning-flow-scanner-core 6.10.6 → 6.11.1
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 +73 -60
- 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/models/Flow.d.ts +8 -0
- package/main/models/Flow.js +57 -2
- 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/rules/DuplicateDMLOperation.d.ts +1 -1
- package/main/rules/DuplicateDMLOperation.js +36 -67
- package/main/rules/MissingFaultPath.js +4 -14
- package/main/rules/TransformInsteadOfLoop.js +15 -16
- package/main/rules/UnconnectedElement.js +2 -9
- package/package.json +2 -2
- package/CONTRIBUTING.md +0 -31
- package/SECURITY.md +0 -55
- package/docs/media/banner.png +0 -0
|
@@ -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/models/Flow.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FlowElement } from "./FlowElement";
|
|
2
2
|
import { FlowNode } from "./FlowNode";
|
|
3
|
+
import { FlowGraph } from "./FlowGraph";
|
|
3
4
|
export declare class Flow {
|
|
4
5
|
/**
|
|
5
6
|
* Metadata Tags of Salesforce Flow Attributes
|
|
@@ -42,11 +43,18 @@ export declare class Flow {
|
|
|
42
43
|
* Access start element data via startNode.element
|
|
43
44
|
*/
|
|
44
45
|
startNode?: FlowNode;
|
|
46
|
+
private _graph?;
|
|
47
|
+
get graph(): FlowGraph;
|
|
45
48
|
root?: any;
|
|
46
49
|
xmldata: any;
|
|
47
50
|
constructor(path?: string, data?: unknown);
|
|
48
51
|
static from(obj: Partial<Flow>): Flow;
|
|
49
52
|
preProcessNodes(): void;
|
|
53
|
+
visualize(format?: 'mermaid' | 'plantuml', options?: {
|
|
54
|
+
includeDetails?: boolean;
|
|
55
|
+
includeMarkdownDocs?: boolean;
|
|
56
|
+
collapsedDetails?: boolean;
|
|
57
|
+
}): string;
|
|
50
58
|
private processNodeType;
|
|
51
59
|
/**
|
|
52
60
|
* Find the name of the first element to execute.
|
package/main/models/Flow.js
CHANGED
|
@@ -14,6 +14,7 @@ const _FlowMetadata = require("./FlowMetadata");
|
|
|
14
14
|
const _FlowNode = require("./FlowNode");
|
|
15
15
|
const _FlowResource = require("./FlowResource");
|
|
16
16
|
const _FlowVariable = require("./FlowVariable");
|
|
17
|
+
const _FlowGraph = require("./FlowGraph");
|
|
17
18
|
function _define_property(obj, key, value) {
|
|
18
19
|
if (key in obj) {
|
|
19
20
|
Object.defineProperty(obj, key, {
|
|
@@ -83,7 +84,39 @@ function _object_spread(target) {
|
|
|
83
84
|
}
|
|
84
85
|
return target;
|
|
85
86
|
}
|
|
87
|
+
function ownKeys(object, enumerableOnly) {
|
|
88
|
+
var keys = Object.keys(object);
|
|
89
|
+
if (Object.getOwnPropertySymbols) {
|
|
90
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
91
|
+
if (enumerableOnly) {
|
|
92
|
+
symbols = symbols.filter(function(sym) {
|
|
93
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
keys.push.apply(keys, symbols);
|
|
97
|
+
}
|
|
98
|
+
return keys;
|
|
99
|
+
}
|
|
100
|
+
function _object_spread_props(target, source) {
|
|
101
|
+
source = source != null ? source : {};
|
|
102
|
+
if (Object.getOwnPropertyDescriptors) {
|
|
103
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
104
|
+
} else {
|
|
105
|
+
ownKeys(Object(source)).forEach(function(key) {
|
|
106
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return target;
|
|
110
|
+
}
|
|
86
111
|
let Flow = class Flow {
|
|
112
|
+
get graph() {
|
|
113
|
+
if (!this._graph) {
|
|
114
|
+
const flowNodes = this.elements.filter((e)=>e instanceof _FlowNode.FlowNode);
|
|
115
|
+
this.startReference || (this.startReference = this.findStart());
|
|
116
|
+
this._graph = new _FlowGraph.FlowGraph(flowNodes, this.startReference, this.startNode);
|
|
117
|
+
}
|
|
118
|
+
return this._graph;
|
|
119
|
+
}
|
|
87
120
|
static from(obj) {
|
|
88
121
|
if (obj instanceof Flow) {
|
|
89
122
|
return obj;
|
|
@@ -115,14 +148,14 @@ let Flow = class Flow {
|
|
|
115
148
|
continue;
|
|
116
149
|
}
|
|
117
150
|
const data = this.xmldata[nodeType];
|
|
118
|
-
// Handle start nodes separately - store in startNode
|
|
151
|
+
// Handle start nodes separately - store in startNode property
|
|
119
152
|
if (nodeType === "start") {
|
|
120
153
|
if (Array.isArray(data) && data.length > 0) {
|
|
121
154
|
this.startNode = new _FlowNode.FlowNode(data[0].name || "start", "start", data[0]);
|
|
122
155
|
} else if (!Array.isArray(data)) {
|
|
123
156
|
this.startNode = new _FlowNode.FlowNode(data.name || "start", "start", data);
|
|
124
157
|
}
|
|
125
|
-
continue;
|
|
158
|
+
continue;
|
|
126
159
|
}
|
|
127
160
|
// Process other node types
|
|
128
161
|
if (Flow.ATTRIBUTE_TAGS.includes(nodeType)) {
|
|
@@ -137,6 +170,27 @@ let Flow = class Flow {
|
|
|
137
170
|
}
|
|
138
171
|
this.elements = allNodes;
|
|
139
172
|
this.startReference = this.findStart();
|
|
173
|
+
// Build the connectivity graph
|
|
174
|
+
const flowNodes = allNodes.filter((e)=>e instanceof _FlowNode.FlowNode);
|
|
175
|
+
this._graph = new _FlowGraph.FlowGraph(flowNodes, this.startReference, this.startNode);
|
|
176
|
+
}
|
|
177
|
+
visualize(format = 'mermaid', options = {}) {
|
|
178
|
+
if (format === 'mermaid') {
|
|
179
|
+
var _this_xmldata, _this_startNode_element, _this_startNode, _this_startNode_element1, _this_startNode1;
|
|
180
|
+
return this.graph.toMermaid(_object_spread_props(_object_spread({}, options), {
|
|
181
|
+
flowMetadata: {
|
|
182
|
+
label: this.label,
|
|
183
|
+
processType: this.processType,
|
|
184
|
+
status: this.status,
|
|
185
|
+
description: (_this_xmldata = this.xmldata) === null || _this_xmldata === void 0 ? void 0 : _this_xmldata.description,
|
|
186
|
+
triggerType: (_this_startNode = this.startNode) === null || _this_startNode === void 0 ? void 0 : (_this_startNode_element = _this_startNode.element) === null || _this_startNode_element === void 0 ? void 0 : _this_startNode_element['triggerType'],
|
|
187
|
+
object: (_this_startNode1 = this.startNode) === null || _this_startNode1 === void 0 ? void 0 : (_this_startNode_element1 = _this_startNode1.element) === null || _this_startNode_element1 === void 0 ? void 0 : _this_startNode_element1['object']
|
|
188
|
+
}
|
|
189
|
+
}));
|
|
190
|
+
} else if (format === 'plantuml') {
|
|
191
|
+
return this.graph.toPlantUML();
|
|
192
|
+
}
|
|
193
|
+
throw new Error('Unsupported format');
|
|
140
194
|
}
|
|
141
195
|
processNodeType(data, nodeType, allNodes, NodeClass) {
|
|
142
196
|
if (Array.isArray(data)) {
|
|
@@ -248,6 +302,7 @@ let Flow = class Flow {
|
|
|
248
302
|
* Contains trigger information and connectors.
|
|
249
303
|
* Access start element data via startNode.element
|
|
250
304
|
*/ _define_property(this, "startNode", void 0);
|
|
305
|
+
_define_property(this, "_graph", void 0);
|
|
251
306
|
// Legacy/internal
|
|
252
307
|
_define_property(this, "root", void 0);
|
|
253
308
|
_define_property(this, "xmldata", void 0);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { FlowNode } from "./FlowNode";
|
|
2
|
+
/**
|
|
3
|
+
* FlowGraph: Pre-computed connectivity cache built using Compiler.
|
|
4
|
+
* Built once during Flow.preProcessNodes() to avoid repeated traversals.
|
|
5
|
+
*
|
|
6
|
+
* Uses the existing Compiler to build the graph - no duplicate logic!
|
|
7
|
+
*/
|
|
8
|
+
export declare class FlowGraph {
|
|
9
|
+
private nodeMap;
|
|
10
|
+
private reachableFromStart;
|
|
11
|
+
private elementsInLoop;
|
|
12
|
+
private faultConnectors;
|
|
13
|
+
private normalConnectors;
|
|
14
|
+
private allConnectors;
|
|
15
|
+
private reverseConnectors;
|
|
16
|
+
constructor(nodes: FlowNode[], startReference?: string, startNode?: FlowNode);
|
|
17
|
+
/**
|
|
18
|
+
* Add START node connectors to the connector maps (for flows with explicit <start> element)
|
|
19
|
+
*/
|
|
20
|
+
private addStartNodeConnectors;
|
|
21
|
+
/**
|
|
22
|
+
* Add START edge for newer flows that use startElementReference (no explicit <start> node)
|
|
23
|
+
*/
|
|
24
|
+
private addStartEdgeFromReference;
|
|
25
|
+
/**
|
|
26
|
+
* Build node map for O(1) lookups
|
|
27
|
+
*/
|
|
28
|
+
private buildNodeMaps;
|
|
29
|
+
/**
|
|
30
|
+
* Build connector maps by inspecting node connectors
|
|
31
|
+
*/
|
|
32
|
+
private buildConnectorMaps;
|
|
33
|
+
/**
|
|
34
|
+
* Use Compiler to compute which elements are reachable from start.
|
|
35
|
+
* This reuses the existing IDDFS traversal logic!
|
|
36
|
+
*/
|
|
37
|
+
private computeReachability;
|
|
38
|
+
/**
|
|
39
|
+
* Use Compiler to compute which elements are inside loops.
|
|
40
|
+
* Calls Compiler.traverseFlow() for each loop with endElementName.
|
|
41
|
+
*/
|
|
42
|
+
private computeLoopBoundaries;
|
|
43
|
+
isReachable(elementName: string): boolean;
|
|
44
|
+
getReachableElements(): Set<string>;
|
|
45
|
+
isInLoop(elementName: string): boolean;
|
|
46
|
+
getContainingLoop(elementName: string): string | undefined;
|
|
47
|
+
getLoopElements(loopName: string): Set<string>;
|
|
48
|
+
hasFaultConnector(elementName: string): boolean;
|
|
49
|
+
getFaultTargets(elementName: string): string[];
|
|
50
|
+
getNextElements(elementName: string): string[];
|
|
51
|
+
getAllNextElements(elementName: string): string[];
|
|
52
|
+
getPreviousElements(elementName: string): string[];
|
|
53
|
+
getNode(elementName: string): FlowNode | undefined;
|
|
54
|
+
isPartOfFaultHandling(elementName: string): boolean;
|
|
55
|
+
getLoopNodes(): FlowNode[];
|
|
56
|
+
forEachReachable(callback: (node: FlowNode) => void): void;
|
|
57
|
+
/**
|
|
58
|
+
* Export the graph to Mermaid flowchart syntax with rich documentation.
|
|
59
|
+
*/
|
|
60
|
+
toMermaid(options?: {
|
|
61
|
+
includeDetails?: boolean;
|
|
62
|
+
includeMarkdownDocs?: boolean;
|
|
63
|
+
collapsedDetails?: boolean;
|
|
64
|
+
flowMetadata?: any;
|
|
65
|
+
}): string;
|
|
66
|
+
private generateMermaidDiagram;
|
|
67
|
+
private generateStartNode;
|
|
68
|
+
private getNodeShape;
|
|
69
|
+
private generateEdges;
|
|
70
|
+
private findEndNodes;
|
|
71
|
+
private generateLoopSubgraphs;
|
|
72
|
+
private generateMermaidStyles;
|
|
73
|
+
private generateNodeDetailsMarkdown;
|
|
74
|
+
private nodeToMarkdownTable;
|
|
75
|
+
private prettifyValue;
|
|
76
|
+
/**
|
|
77
|
+
* Generate full markdown documentation with diagram and node details
|
|
78
|
+
*/
|
|
79
|
+
private generateFullMarkdownDoc;
|
|
80
|
+
/**
|
|
81
|
+
* Export the graph to PlantUML syntax for UML-style diagrams.
|
|
82
|
+
* @returns PlantUML string.
|
|
83
|
+
*/
|
|
84
|
+
toPlantUML(): string;
|
|
85
|
+
}
|