@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.
@@ -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
+ }
@@ -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.
@@ -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, don't add to elements
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; // Don't add to elements array
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
+ }