@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.
Files changed (38) hide show
  1. package/README.md +19 -16
  2. package/index.d.ts +5 -2
  3. package/index.js +19 -0
  4. package/main/config/NodeIcons.d.ts +24 -0
  5. package/main/config/NodeIcons.js +122 -0
  6. package/main/config/VariableIcons.d.ts +25 -0
  7. package/main/config/VariableIcons.js +53 -0
  8. package/main/libs/Compiler.d.ts +1 -2
  9. package/main/libs/Compiler.js +10 -16
  10. package/main/libs/ExportDiagram.d.ts +41 -0
  11. package/main/libs/ExportDiagram.js +40 -0
  12. package/main/libs/ExportSarif.js +1 -1
  13. package/main/models/Flow.d.ts +42 -11
  14. package/main/models/Flow.js +164 -76
  15. package/main/models/FlowGraph.d.ts +85 -0
  16. package/main/models/FlowGraph.js +532 -0
  17. package/main/models/FlowNode.d.ts +58 -2
  18. package/main/models/FlowNode.js +161 -3
  19. package/main/models/FlowVariable.d.ts +59 -1
  20. package/main/models/FlowVariable.js +118 -1
  21. package/main/models/LoopRuleCommon.js +11 -12
  22. package/main/models/ParsedFlow.d.ts +1 -1
  23. package/main/models/RuleCommon.d.ts +30 -7
  24. package/main/models/RuleCommon.js +49 -11
  25. package/main/rules/APIVersion.js +31 -1
  26. package/main/rules/DuplicateDMLOperation.d.ts +1 -2
  27. package/main/rules/DuplicateDMLOperation.js +35 -73
  28. package/main/rules/MissingFaultPath.d.ts +4 -0
  29. package/main/rules/MissingFaultPath.js +19 -15
  30. package/main/rules/MissingFilterRecordTrigger.js +4 -4
  31. package/main/rules/RecordIdAsString.js +3 -2
  32. package/main/rules/RecursiveAfterUpdate.js +7 -4
  33. package/main/rules/SameRecordFieldUpdates.js +5 -3
  34. package/main/rules/TriggerOrder.d.ts +0 -1
  35. package/main/rules/TriggerOrder.js +8 -19
  36. package/main/rules/UnconnectedElement.d.ts +0 -1
  37. package/main/rules/UnconnectedElement.js +2 -13
  38. 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/blob/main/LICENSE.md">
3
- <img src="https://img.shields.io/github/license/Flow-Scanner/lightning-flow-scanner?style=flat-square" alt="License">
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
- <a href="https://www.npmjs.com/package/@flow-scanner/lightning-flow-scanner-core">
9
- <img src="https://img.shields.io/npm/v/@flow-scanner/lightning-flow-scanner-core?label=core&style=flat-square" alt="Core version">
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=cli&style=flat-square" alt="CLI version">
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=Open%20VSX&style=flat-square" alt="Open VSX">
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
- <img src="https://img.shields.io/badge/downloads-510k%2B-success?style=flat-square" alt="Total Downloads">
22
- </a>
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/*.xml").then(scan).then(exportSarif);
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
- export { Compiler, exportDetails, exportSarif, fix, Flow, FlowAttribute, FlowElement, FlowNode, FlowResource, FlowType, FlowVariable, getRules, parse, ParsedFlow, Violation, RuleResult, scan, ScanResult, };
23
- export type { FlatViolation, IRuleDefinition, IRulesConfig };
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
+ }
@@ -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(flow: Flow, startElementName: string, visitCallback: (element: FlowNode) => void, endElementName?: string): void;
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
  }
@@ -22,7 +22,7 @@ function _define_property(obj, key, value) {
22
22
  return obj;
23
23
  }
24
24
  let Compiler = class Compiler {
25
- traverseFlow(flow, startElementName, visitCallback, endElementName) {
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
- var _flow_elements;
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(flow, currentElement, endElementName));
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(flow, currentElement, endElementName) {
48
+ findNextElements(elementName, allConnectors, nodeMap, endElementName) {
50
49
  const nextElements = [];
51
- if (!currentElement.connectors || currentElement.connectors.length === 0) {
52
- return nextElements;
53
- }
54
- for (const connector of currentElement.connectors){
55
- var _connector_connectorTargetReference, _flow_elements;
56
- var _connector_connectorTargetReference_targetReference;
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
+ }
@@ -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-core",
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: {
@@ -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", "start", "steps", "subflows", "waits", "transforms", "customErrors"];
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?: FlowElement[];
15
+ elements: FlowElement[];
14
16
  fsPath?: string;
15
17
  uri?: string;
16
- interviewLabel?: string;
17
18
  label: string;
18
- name?: string;
19
+ interviewLabel?: string;
20
+ name: string;
19
21
  processMetadataValues?: any;
20
- processType?: string;
21
- root?: any;
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
- * XML to JSON conversion in raw format
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
- toXMLString(): string;
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
  }