@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/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;
|
|
@@ -96,14 +129,17 @@ let Flow = class Flow {
|
|
|
96
129
|
return flow;
|
|
97
130
|
}
|
|
98
131
|
preProcessNodes() {
|
|
99
|
-
|
|
132
|
+
if (!this.xmldata) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// Extract top-level attributes
|
|
136
|
+
this.label = this.xmldata.label || "";
|
|
100
137
|
this.interviewLabel = this.xmldata.interviewLabel;
|
|
101
|
-
this.processType = this.xmldata.processType;
|
|
138
|
+
this.processType = this.xmldata.processType || "AutoLaunchedFlow";
|
|
139
|
+
this.type = this.processType;
|
|
102
140
|
this.processMetadataValues = this.xmldata.processMetadataValues;
|
|
103
141
|
this.startElementReference = this.xmldata.startElementReference;
|
|
104
|
-
this.
|
|
105
|
-
this.status = this.xmldata.status;
|
|
106
|
-
this.type = this.xmldata.processType;
|
|
142
|
+
this.status = this.xmldata.status || "Draft";
|
|
107
143
|
this.triggerOrder = this.xmldata.triggerOrder;
|
|
108
144
|
const allNodes = [];
|
|
109
145
|
for(const nodeType in this.xmldata){
|
|
@@ -112,129 +148,182 @@ let Flow = class Flow {
|
|
|
112
148
|
continue;
|
|
113
149
|
}
|
|
114
150
|
const data = this.xmldata[nodeType];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
allNodes.push(new _FlowMetadata.FlowMetadata(data.name, nodeType, data));
|
|
151
|
+
// Handle start nodes separately - store in startNode property
|
|
152
|
+
if (nodeType === "start") {
|
|
153
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
154
|
+
this.startNode = new _FlowNode.FlowNode(data[0].name || "start", "start", data[0]);
|
|
155
|
+
} else if (!Array.isArray(data)) {
|
|
156
|
+
this.startNode = new _FlowNode.FlowNode(data.name || "start", "start", data);
|
|
122
157
|
}
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
// Process other node types
|
|
161
|
+
if (Flow.ATTRIBUTE_TAGS.includes(nodeType)) {
|
|
162
|
+
this.processNodeType(data, nodeType, allNodes, _FlowMetadata.FlowMetadata);
|
|
123
163
|
} else if (Flow.VARIABLE_TAGS.includes(nodeType)) {
|
|
124
|
-
|
|
125
|
-
for (const node of data){
|
|
126
|
-
allNodes.push(new _FlowVariable.FlowVariable(node.name, nodeType, node));
|
|
127
|
-
}
|
|
128
|
-
} else {
|
|
129
|
-
allNodes.push(new _FlowVariable.FlowVariable(data.name, nodeType, data));
|
|
130
|
-
}
|
|
164
|
+
this.processNodeType(data, nodeType, allNodes, _FlowVariable.FlowVariable);
|
|
131
165
|
} else if (Flow.NODE_TAGS.includes(nodeType)) {
|
|
132
|
-
|
|
133
|
-
for (const node of data){
|
|
134
|
-
allNodes.push(new _FlowNode.FlowNode(node.name, nodeType, node));
|
|
135
|
-
}
|
|
136
|
-
} else {
|
|
137
|
-
allNodes.push(new _FlowNode.FlowNode(data.name, nodeType, data));
|
|
138
|
-
}
|
|
166
|
+
this.processNodeType(data, nodeType, allNodes, _FlowNode.FlowNode);
|
|
139
167
|
} else if (Flow.RESOURCE_TAGS.includes(nodeType)) {
|
|
140
|
-
|
|
141
|
-
for (const node of data){
|
|
142
|
-
allNodes.push(new _FlowResource.FlowResource(node.name, nodeType, node));
|
|
143
|
-
}
|
|
144
|
-
} else {
|
|
145
|
-
allNodes.push(new _FlowResource.FlowResource(data.name, nodeType, data));
|
|
146
|
-
}
|
|
168
|
+
this.processNodeType(data, nodeType, allNodes, _FlowResource.FlowResource);
|
|
147
169
|
}
|
|
148
170
|
}
|
|
149
171
|
this.elements = allNodes;
|
|
150
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');
|
|
194
|
+
}
|
|
195
|
+
processNodeType(data, nodeType, allNodes, NodeClass) {
|
|
196
|
+
if (Array.isArray(data)) {
|
|
197
|
+
for (const node of data){
|
|
198
|
+
allNodes.push(new NodeClass(node.name, nodeType, node));
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
allNodes.push(new NodeClass(data.name, nodeType, data));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Find the name of the first element to execute.
|
|
206
|
+
* Priority order:
|
|
207
|
+
* 1. startElementReference (newer flows, direct XML attribute)
|
|
208
|
+
* 2. Start node connector (older flows, points to first element)
|
|
209
|
+
* 3. Start node scheduledPaths (async flows)
|
|
210
|
+
*/ findStart() {
|
|
211
|
+
var _this_startNode;
|
|
212
|
+
// Priority 1: Explicit startElementReference
|
|
213
|
+
if (this.startElementReference) {
|
|
214
|
+
return this.startElementReference;
|
|
215
|
+
}
|
|
216
|
+
// Priority 2: Start node with regular connector
|
|
217
|
+
if (this.startNode && this.startNode.connectors && this.startNode.connectors.length > 0) {
|
|
218
|
+
const connector = this.startNode.connectors[0];
|
|
219
|
+
if (connector.reference) {
|
|
220
|
+
return connector.reference;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Priority 3: Start node with scheduledPaths (async flows)
|
|
224
|
+
if ((_this_startNode = this.startNode) === null || _this_startNode === void 0 ? void 0 : _this_startNode.element) {
|
|
225
|
+
const scheduledPaths = this.startNode.element['scheduledPaths'];
|
|
226
|
+
if (scheduledPaths) {
|
|
227
|
+
var _paths_;
|
|
228
|
+
const paths = Array.isArray(scheduledPaths) ? scheduledPaths : [
|
|
229
|
+
scheduledPaths
|
|
230
|
+
];
|
|
231
|
+
if (paths.length > 0 && ((_paths_ = paths[0]) === null || _paths_ === void 0 ? void 0 : _paths_.connector)) {
|
|
232
|
+
const targetRef = paths[0].connector.targetReference;
|
|
233
|
+
if (targetRef) {
|
|
234
|
+
return targetRef;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// No valid start found
|
|
240
|
+
return "";
|
|
151
241
|
}
|
|
152
242
|
toXMLString() {
|
|
153
243
|
try {
|
|
154
244
|
return this.generateDoc();
|
|
155
245
|
} catch (exception) {
|
|
156
|
-
|
|
246
|
+
const errorMsg = exception instanceof Error ? exception.message : String(exception);
|
|
247
|
+
console.warn(`Unable to write xml, caught an error: ${errorMsg}`);
|
|
157
248
|
return "";
|
|
158
249
|
}
|
|
159
250
|
}
|
|
160
|
-
findStart() {
|
|
161
|
-
let start = "";
|
|
162
|
-
const flowElements = this.elements.filter((node)=>node instanceof _FlowNode.FlowNode);
|
|
163
|
-
if (this.startElementReference) {
|
|
164
|
-
start = this.startElementReference;
|
|
165
|
-
} else if (flowElements.find((n)=>{
|
|
166
|
-
return n.subtype === "start";
|
|
167
|
-
})) {
|
|
168
|
-
const startElement = flowElements.find((n)=>{
|
|
169
|
-
return n.subtype === "start";
|
|
170
|
-
});
|
|
171
|
-
start = startElement.connectors[0]["reference"];
|
|
172
|
-
}
|
|
173
|
-
return start;
|
|
174
|
-
}
|
|
175
251
|
generateDoc() {
|
|
176
|
-
// eslint-disable-next-line sonarjs/no-clear-text-protocols
|
|
177
252
|
const flowXmlNamespace = "http://soap.sforce.com/2006/04/metadata";
|
|
178
253
|
const builderOptions = {
|
|
179
254
|
attributeNamePrefix: "@_",
|
|
180
255
|
format: true,
|
|
181
256
|
ignoreAttributes: false,
|
|
182
257
|
suppressBooleanAttributes: false,
|
|
183
|
-
suppressEmptyNode: false
|
|
258
|
+
suppressEmptyNode: false
|
|
184
259
|
};
|
|
185
260
|
const builder = new _fastxmlparser.XMLBuilder(builderOptions);
|
|
186
|
-
// Fallback: Inject xmlns as attribute if missing
|
|
187
261
|
const xmldataWithNs = _object_spread({}, this.xmldata);
|
|
188
262
|
if (!xmldataWithNs["@_xmlns"]) {
|
|
189
263
|
xmldataWithNs["@_xmlns"] = flowXmlNamespace;
|
|
190
264
|
}
|
|
191
|
-
// Optional: Add xsi if needed (often in parsed data; test has it in root)
|
|
192
265
|
if (!xmldataWithNs["@_xmlns:xsi"]) {
|
|
193
266
|
xmldataWithNs["@_xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance";
|
|
194
267
|
}
|
|
195
|
-
// Build: Wrap in { Flow: ... }
|
|
196
268
|
const rootObj = {
|
|
197
269
|
Flow: xmldataWithNs
|
|
198
270
|
};
|
|
199
271
|
return builder.build(rootObj);
|
|
200
272
|
}
|
|
201
273
|
constructor(path, data){
|
|
202
|
-
|
|
203
|
-
_define_property(this, "
|
|
204
|
-
|
|
274
|
+
// Flow elements (excludes legacy start nodes)
|
|
275
|
+
_define_property(this, "elements", []);
|
|
276
|
+
// Path properties
|
|
277
|
+
_define_property(this, "fsPath", void 0); // Resolved absolute path (Node.js only)
|
|
278
|
+
_define_property(this, "uri", void 0); // Input path (could be relative, absolute, or virtual)
|
|
279
|
+
// Flow metadata
|
|
280
|
+
_define_property(this, "label", "");
|
|
205
281
|
_define_property(this, "interviewLabel", void 0);
|
|
206
|
-
_define_property(this, "
|
|
207
|
-
_define_property(this, "name", void 0);
|
|
282
|
+
_define_property(this, "name", "unnamed");
|
|
208
283
|
_define_property(this, "processMetadataValues", void 0);
|
|
209
|
-
_define_property(this, "processType",
|
|
210
|
-
_define_property(this, "
|
|
211
|
-
_define_property(this, "
|
|
212
|
-
_define_property(this, "startElementReference", void 0);
|
|
213
|
-
_define_property(this, "startReference", void 0);
|
|
214
|
-
_define_property(this, "status", void 0);
|
|
284
|
+
_define_property(this, "processType", "AutoLaunchedFlow");
|
|
285
|
+
_define_property(this, "type", ""); // Alias for processType (backward compatibility)
|
|
286
|
+
_define_property(this, "status", "");
|
|
215
287
|
_define_property(this, "triggerOrder", void 0);
|
|
216
|
-
|
|
288
|
+
// Start-related properties
|
|
217
289
|
/**
|
|
218
|
-
*
|
|
219
|
-
*/ _define_property(this, "
|
|
290
|
+
* @deprecated Use startNode.element instead. Kept for backward compatibility.
|
|
291
|
+
*/ _define_property(this, "start", void 0);
|
|
292
|
+
/**
|
|
293
|
+
* Direct reference to first element (from XML attribute).
|
|
294
|
+
* Used in newer flows as an alternative to the start element.
|
|
295
|
+
*/ _define_property(this, "startElementReference", void 0);
|
|
296
|
+
/**
|
|
297
|
+
* Computed reference to the first element to execute.
|
|
298
|
+
* This is what rules should use for traversal.
|
|
299
|
+
*/ _define_property(this, "startReference", void 0);
|
|
300
|
+
/**
|
|
301
|
+
* Parsed FlowNode object of the start element.
|
|
302
|
+
* Contains trigger information and connectors.
|
|
303
|
+
* Access start element data via startNode.element
|
|
304
|
+
*/ _define_property(this, "startNode", void 0);
|
|
305
|
+
_define_property(this, "_graph", void 0);
|
|
306
|
+
// Legacy/internal
|
|
307
|
+
_define_property(this, "root", void 0);
|
|
308
|
+
_define_property(this, "xmldata", void 0);
|
|
220
309
|
if (path) {
|
|
221
|
-
this.uri = path;
|
|
222
|
-
|
|
223
|
-
// In browser with polyfills, fsPath stays undefined
|
|
224
|
-
if (typeof process !== 'undefined' && process.cwd) {
|
|
310
|
+
this.uri = path;
|
|
311
|
+
if (typeof process !== 'undefined' && typeof process.cwd === 'function') {
|
|
225
312
|
this.fsPath = _path.resolve(path);
|
|
226
313
|
}
|
|
227
314
|
let flowName = _path.basename(_path.basename(path), _path.extname(path));
|
|
228
315
|
if (flowName.includes(".")) {
|
|
229
316
|
flowName = flowName.split(".")[0];
|
|
230
317
|
}
|
|
231
|
-
this.name = flowName;
|
|
318
|
+
this.name = flowName || "unnamed";
|
|
232
319
|
}
|
|
233
320
|
if (data) {
|
|
234
|
-
const hasFlowElement = typeof data === "object" && "Flow" in data;
|
|
321
|
+
const hasFlowElement = typeof data === "object" && data !== null && "Flow" in data;
|
|
235
322
|
if (hasFlowElement) {
|
|
236
323
|
this.xmldata = data.Flow;
|
|
237
|
-
} else
|
|
324
|
+
} else {
|
|
325
|
+
this.xmldata = data;
|
|
326
|
+
}
|
|
238
327
|
this.preProcessNodes();
|
|
239
328
|
}
|
|
240
329
|
}
|
|
@@ -276,7 +365,6 @@ let Flow = class Flow {
|
|
|
276
365
|
"recordUpdates",
|
|
277
366
|
"recordRollbacks",
|
|
278
367
|
"screens",
|
|
279
|
-
"start",
|
|
280
368
|
"steps",
|
|
281
369
|
"subflows",
|
|
282
370
|
"waits",
|
|
@@ -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
|
+
}
|