@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/FlowNode.js
CHANGED
|
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "FlowNode", {
|
|
|
11
11
|
const _MetadataTypes = require("../enums/MetadataTypes");
|
|
12
12
|
const _FlowElement = require("./FlowElement");
|
|
13
13
|
const _FlowElementConnector = require("./FlowElementConnector");
|
|
14
|
+
const _NodeIcons = require("../config/NodeIcons");
|
|
14
15
|
function _define_property(obj, key, value) {
|
|
15
16
|
if (key in obj) {
|
|
16
17
|
Object.defineProperty(obj, key, {
|
|
@@ -25,6 +26,146 @@ function _define_property(obj, key, value) {
|
|
|
25
26
|
return obj;
|
|
26
27
|
}
|
|
27
28
|
let FlowNode = class FlowNode extends _FlowElement.FlowElement {
|
|
29
|
+
/**
|
|
30
|
+
* Set custom icon configuration for all FlowNodes
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* // Use ASCII icons for old terminals
|
|
34
|
+
* FlowNode.setIconConfig(ASCII_ICONS);
|
|
35
|
+
*
|
|
36
|
+
* // Or provide custom icons
|
|
37
|
+
* FlowNode.setIconConfig({
|
|
38
|
+
* actionCalls: { default: '[ACTION]' },
|
|
39
|
+
* decisions: { default: '[IF]' }
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/ static setIconConfig(config) {
|
|
43
|
+
FlowNode.iconConfig = config;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Use ASCII icons instead of emoji (for older browsers/terminals)
|
|
47
|
+
*/ static useAsciiIcons() {
|
|
48
|
+
FlowNode.iconConfig = _NodeIcons.ASCII_ICONS;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Reset to default emoji icons
|
|
52
|
+
*/ static useDefaultIcons() {
|
|
53
|
+
FlowNode.iconConfig = _NodeIcons.DEFAULT_ICONS;
|
|
54
|
+
}
|
|
55
|
+
extractTypeSpecificProperties(subtype, element) {
|
|
56
|
+
switch(subtype){
|
|
57
|
+
case "actionCalls":
|
|
58
|
+
this.actionType = element.actionType;
|
|
59
|
+
this.actionName = element.actionName;
|
|
60
|
+
break;
|
|
61
|
+
case "recordCreates":
|
|
62
|
+
case "recordUpdates":
|
|
63
|
+
case "recordDeletes":
|
|
64
|
+
case "recordLookups":
|
|
65
|
+
this.object = element.object;
|
|
66
|
+
this.inputReference = element.inputReference;
|
|
67
|
+
this.outputReference = element.outputReference;
|
|
68
|
+
break;
|
|
69
|
+
case "collectionProcessors":
|
|
70
|
+
this.elementSubtype = element.elementSubtype;
|
|
71
|
+
this.collectionReference = element.collectionReference;
|
|
72
|
+
break;
|
|
73
|
+
case "subflows":
|
|
74
|
+
this.flowName = element.flowName;
|
|
75
|
+
break;
|
|
76
|
+
case "decisions":
|
|
77
|
+
this.rules = Array.isArray(element.rules) ? element.rules : element.rules ? [
|
|
78
|
+
element.rules
|
|
79
|
+
] : [];
|
|
80
|
+
this.defaultConnectorLabel = element.defaultConnectorLabel;
|
|
81
|
+
break;
|
|
82
|
+
case "loops":
|
|
83
|
+
this.collectionReference = element.collectionReference;
|
|
84
|
+
this.iterationOrder = element.iterationOrder;
|
|
85
|
+
break;
|
|
86
|
+
case "screens":
|
|
87
|
+
this.fields = Array.isArray(element.fields) ? element.fields : element.fields ? [
|
|
88
|
+
element.fields
|
|
89
|
+
] : [];
|
|
90
|
+
this.allowPause = element.allowPause;
|
|
91
|
+
this.showFooter = element.showFooter;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get a human-readable summary of this node
|
|
97
|
+
*/ getSummary() {
|
|
98
|
+
const parts = [];
|
|
99
|
+
switch(this.subtype){
|
|
100
|
+
case "actionCalls":
|
|
101
|
+
if (this.actionType) parts.push(this.prettifyValue(this.actionType));
|
|
102
|
+
if (this.actionName) parts.push(this.actionName);
|
|
103
|
+
break;
|
|
104
|
+
case "recordCreates":
|
|
105
|
+
case "recordUpdates":
|
|
106
|
+
case "recordDeletes":
|
|
107
|
+
case "recordLookups":
|
|
108
|
+
if (this.object) parts.push(this.object);
|
|
109
|
+
break;
|
|
110
|
+
case "collectionProcessors":
|
|
111
|
+
if (this.elementSubtype) parts.push(this.prettifyValue(this.elementSubtype));
|
|
112
|
+
break;
|
|
113
|
+
case "decisions":
|
|
114
|
+
var _this_rules, _this_rules1;
|
|
115
|
+
parts.push(`${((_this_rules = this.rules) === null || _this_rules === void 0 ? void 0 : _this_rules.length) || 0} rule${((_this_rules1 = this.rules) === null || _this_rules1 === void 0 ? void 0 : _this_rules1.length) !== 1 ? 's' : ''}`);
|
|
116
|
+
break;
|
|
117
|
+
case "loops":
|
|
118
|
+
if (this.collectionReference) parts.push(`Loop: ${this.collectionReference}`);
|
|
119
|
+
break;
|
|
120
|
+
case "subflows":
|
|
121
|
+
if (this.flowName) parts.push(this.flowName);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
if (this.description) {
|
|
125
|
+
parts.push(this.description.substring(0, 50) + (this.description.length > 50 ? '...' : ''));
|
|
126
|
+
}
|
|
127
|
+
return parts.join(' • ');
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get the icon for this node type
|
|
131
|
+
*/ getIcon() {
|
|
132
|
+
const typeIcons = FlowNode.iconConfig[this.subtype];
|
|
133
|
+
if (!typeIcons) {
|
|
134
|
+
// Fallback for unknown types
|
|
135
|
+
const fallback = FlowNode.iconConfig['default'];
|
|
136
|
+
return fallback && 'default' in fallback ? fallback.default : '\u2022'; // • BULLET
|
|
137
|
+
}
|
|
138
|
+
// For nodes with subtypes (like actionCalls or collectionProcessors)
|
|
139
|
+
const subtype = this.actionType || this.elementSubtype;
|
|
140
|
+
const icons = typeIcons;
|
|
141
|
+
if (subtype && icons[subtype]) {
|
|
142
|
+
return icons[subtype];
|
|
143
|
+
}
|
|
144
|
+
return icons.default || '\u2022'; // • BULLET fallback
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get the display name for this node type
|
|
148
|
+
*/ getTypeLabel() {
|
|
149
|
+
const labelMap = {
|
|
150
|
+
actionCalls: 'Action',
|
|
151
|
+
assignments: 'Assignment',
|
|
152
|
+
collectionProcessors: 'Collection',
|
|
153
|
+
customErrors: 'Error',
|
|
154
|
+
decisions: 'Decision',
|
|
155
|
+
loops: 'Loop',
|
|
156
|
+
recordCreates: 'Create',
|
|
157
|
+
recordDeletes: 'Delete',
|
|
158
|
+
recordLookups: 'Get Records',
|
|
159
|
+
recordUpdates: 'Update',
|
|
160
|
+
screens: 'Screen',
|
|
161
|
+
subflows: 'Subflow',
|
|
162
|
+
transforms: 'Transform'
|
|
163
|
+
};
|
|
164
|
+
return labelMap[this.subtype] || this.subtype;
|
|
165
|
+
}
|
|
166
|
+
prettifyValue(value) {
|
|
167
|
+
return value.replace(/([A-Z])/g, ' $1').replace(/^./, (str)=>str.toUpperCase()).trim();
|
|
168
|
+
}
|
|
28
169
|
getConnectors(subtype, element) {
|
|
29
170
|
const connectors = [];
|
|
30
171
|
if (subtype === "start") {
|
|
@@ -160,10 +301,27 @@ let FlowNode = class FlowNode extends _FlowElement.FlowElement {
|
|
|
160
301
|
}
|
|
161
302
|
constructor(provName, subtype, element){
|
|
162
303
|
const nodeName = subtype === "start" ? "flowstart" : provName;
|
|
163
|
-
super(_MetadataTypes.MetaType.NODE, subtype, nodeName, element), _define_property(this, "connectors", []), _define_property(this, "locationX", void 0), _define_property(this, "locationY", void 0)
|
|
164
|
-
|
|
165
|
-
this
|
|
304
|
+
super(_MetadataTypes.MetaType.NODE, subtype, nodeName, element), _define_property(this, "connectors", []), _define_property(this, "locationX", void 0), _define_property(this, "locationY", void 0), // Common properties across node types
|
|
305
|
+
_define_property(this, "label", void 0), _define_property(this, "description", void 0), // Action-specific properties
|
|
306
|
+
_define_property(this, "actionType", void 0), _define_property(this, "actionName", void 0), // Record operation properties
|
|
307
|
+
_define_property(this, "object", void 0), _define_property(this, "inputReference", void 0), _define_property(this, "outputReference", void 0), // Collection processor properties
|
|
308
|
+
_define_property(this, "elementSubtype", void 0), _define_property(this, "collectionReference", void 0), // Subflow properties
|
|
309
|
+
_define_property(this, "flowName", void 0), // Decision properties
|
|
310
|
+
_define_property(this, "rules", void 0), _define_property(this, "defaultConnectorLabel", void 0), // Loop properties
|
|
311
|
+
_define_property(this, "iterationOrder", void 0), // Screen properties
|
|
312
|
+
_define_property(this, "fields", void 0), _define_property(this, "allowPause", void 0), _define_property(this, "showFooter", void 0), // Fault handling
|
|
313
|
+
_define_property(this, "faultConnector", void 0);
|
|
314
|
+
// Extract common properties
|
|
315
|
+
this.label = element["label"];
|
|
316
|
+
this.description = element["description"];
|
|
166
317
|
this.locationX = element["locationX"];
|
|
167
318
|
this.locationY = element["locationY"];
|
|
319
|
+
// Extract type-specific properties
|
|
320
|
+
this.extractTypeSpecificProperties(subtype, element);
|
|
321
|
+
// Extract connectors
|
|
322
|
+
this.connectors = this.getConnectors(subtype, element);
|
|
323
|
+
this.faultConnector = this.connectors.find((c)=>c.type === "faultConnector");
|
|
168
324
|
}
|
|
169
325
|
};
|
|
326
|
+
// Static icon configuration (can be overridden)
|
|
327
|
+
_define_property(FlowNode, "iconConfig", _NodeIcons.DEFAULT_ICONS);
|
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
import { FlowElement } from "./FlowElement";
|
|
2
|
+
import { type VariableIconConfig } from "../config/VariableIcons";
|
|
2
3
|
export declare class FlowVariable extends FlowElement {
|
|
3
|
-
dataType
|
|
4
|
+
dataType?: string;
|
|
5
|
+
isCollection?: boolean;
|
|
6
|
+
isInput?: boolean;
|
|
7
|
+
isOutput?: boolean;
|
|
8
|
+
objectType?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
value?: any;
|
|
11
|
+
private static iconConfig;
|
|
12
|
+
/**
|
|
13
|
+
* Set custom icon configuration for all FlowVariables
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // Use ASCII icons
|
|
17
|
+
* FlowVariable.setIconConfig(ASCII_VARIABLE_ICONS);
|
|
18
|
+
*
|
|
19
|
+
* // Or provide custom icons
|
|
20
|
+
* FlowVariable.setIconConfig({
|
|
21
|
+
* subtypes: {
|
|
22
|
+
* variables: '[VAR]',
|
|
23
|
+
* constants: '[CONST]'
|
|
24
|
+
* },
|
|
25
|
+
* boolean: {
|
|
26
|
+
* true: '[YES]',
|
|
27
|
+
* false: '[NO]'
|
|
28
|
+
* }
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
static setIconConfig(config: VariableIconConfig): void;
|
|
33
|
+
/**
|
|
34
|
+
* Use ASCII icons instead of emoji
|
|
35
|
+
*/
|
|
36
|
+
static useAsciiIcons(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Reset to default emoji icons
|
|
39
|
+
*/
|
|
40
|
+
static useDefaultIcons(): void;
|
|
4
41
|
constructor(name: string, subtype: string, element: object);
|
|
42
|
+
/**
|
|
43
|
+
* Get the icon for this variable subtype
|
|
44
|
+
*/
|
|
45
|
+
getIcon(): string;
|
|
46
|
+
/**
|
|
47
|
+
* Get icon for a boolean value
|
|
48
|
+
*/
|
|
49
|
+
private getBooleanIcon;
|
|
50
|
+
/**
|
|
51
|
+
* Get a human-readable type label
|
|
52
|
+
*/
|
|
53
|
+
getTypeLabel(): string;
|
|
54
|
+
/**
|
|
55
|
+
* Get a markdown table row for this variable
|
|
56
|
+
*/
|
|
57
|
+
toTableRow(): string;
|
|
58
|
+
/**
|
|
59
|
+
* Get a detailed markdown table for this variable
|
|
60
|
+
*/
|
|
61
|
+
toMarkdownTable(): string;
|
|
62
|
+
private formatValue;
|
|
5
63
|
}
|
|
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "FlowVariable", {
|
|
|
10
10
|
});
|
|
11
11
|
const _MetadataTypes = require("../enums/MetadataTypes");
|
|
12
12
|
const _FlowElement = require("./FlowElement");
|
|
13
|
+
const _VariableIcons = require("../config/VariableIcons");
|
|
13
14
|
function _define_property(obj, key, value) {
|
|
14
15
|
if (key in obj) {
|
|
15
16
|
Object.defineProperty(obj, key, {
|
|
@@ -24,8 +25,124 @@ function _define_property(obj, key, value) {
|
|
|
24
25
|
return obj;
|
|
25
26
|
}
|
|
26
27
|
let FlowVariable = class FlowVariable extends _FlowElement.FlowElement {
|
|
28
|
+
/**
|
|
29
|
+
* Set custom icon configuration for all FlowVariables
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // Use ASCII icons
|
|
33
|
+
* FlowVariable.setIconConfig(ASCII_VARIABLE_ICONS);
|
|
34
|
+
*
|
|
35
|
+
* // Or provide custom icons
|
|
36
|
+
* FlowVariable.setIconConfig({
|
|
37
|
+
* subtypes: {
|
|
38
|
+
* variables: '[VAR]',
|
|
39
|
+
* constants: '[CONST]'
|
|
40
|
+
* },
|
|
41
|
+
* boolean: {
|
|
42
|
+
* true: '[YES]',
|
|
43
|
+
* false: '[NO]'
|
|
44
|
+
* }
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/ static setIconConfig(config) {
|
|
48
|
+
FlowVariable.iconConfig = config;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Use ASCII icons instead of emoji
|
|
52
|
+
*/ static useAsciiIcons() {
|
|
53
|
+
FlowVariable.iconConfig = _VariableIcons.ASCII_VARIABLE_ICONS;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Reset to default emoji icons
|
|
57
|
+
*/ static useDefaultIcons() {
|
|
58
|
+
FlowVariable.iconConfig = _VariableIcons.DEFAULT_VARIABLE_ICONS;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the icon for this variable subtype
|
|
62
|
+
*/ getIcon() {
|
|
63
|
+
return FlowVariable.iconConfig.subtypes[this.subtype] || '\uD83D\uDCCA'; // 📊 default
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get icon for a boolean value
|
|
67
|
+
*/ getBooleanIcon(value) {
|
|
68
|
+
if (value === true) {
|
|
69
|
+
return FlowVariable.iconConfig.boolean.true;
|
|
70
|
+
} else if (value === false) {
|
|
71
|
+
return FlowVariable.iconConfig.boolean.false;
|
|
72
|
+
}
|
|
73
|
+
return ''; // undefined/null
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get a human-readable type label
|
|
77
|
+
*/ getTypeLabel() {
|
|
78
|
+
const labelMap = {
|
|
79
|
+
variables: 'Variable',
|
|
80
|
+
constants: 'Constant',
|
|
81
|
+
formulas: 'Formula',
|
|
82
|
+
choices: 'Choice',
|
|
83
|
+
dynamicChoiceSets: 'Dynamic Choice'
|
|
84
|
+
};
|
|
85
|
+
return labelMap[this.subtype] || this.subtype;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get a markdown table row for this variable
|
|
89
|
+
*/ toTableRow() {
|
|
90
|
+
const parts = [
|
|
91
|
+
this.name,
|
|
92
|
+
this.dataType || '',
|
|
93
|
+
this.getBooleanIcon(this.isCollection),
|
|
94
|
+
this.getBooleanIcon(this.isInput),
|
|
95
|
+
this.getBooleanIcon(this.isOutput),
|
|
96
|
+
this.objectType || '',
|
|
97
|
+
this.description || ''
|
|
98
|
+
];
|
|
99
|
+
return `| ${parts.join(' | ')} |`;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get a detailed markdown table for this variable
|
|
103
|
+
*/ toMarkdownTable() {
|
|
104
|
+
let table = '| Property | Value |\n|:---|:---|\n';
|
|
105
|
+
table += `| Name | ${this.name} |\n`;
|
|
106
|
+
table += `| Type | ${this.getIcon()} ${this.getTypeLabel()} |\n`;
|
|
107
|
+
if (this.dataType) table += `| Data Type | ${this.dataType} |\n`;
|
|
108
|
+
if (this.objectType) table += `| Object Type | ${this.objectType} |\n`;
|
|
109
|
+
if (this.isCollection !== undefined) {
|
|
110
|
+
table += `| Collection | ${this.getBooleanIcon(this.isCollection)} |\n`;
|
|
111
|
+
}
|
|
112
|
+
if (this.isInput !== undefined) {
|
|
113
|
+
table += `| Input | ${this.getBooleanIcon(this.isInput)} |\n`;
|
|
114
|
+
}
|
|
115
|
+
if (this.isOutput !== undefined) {
|
|
116
|
+
table += `| Output | ${this.getBooleanIcon(this.isOutput)} |\n`;
|
|
117
|
+
}
|
|
118
|
+
if (this.value !== undefined) {
|
|
119
|
+
table += `| Value | ${this.formatValue(this.value)} |\n`;
|
|
120
|
+
}
|
|
121
|
+
if (this.description) table += `| Description | ${this.description} |\n`;
|
|
122
|
+
return table;
|
|
123
|
+
}
|
|
124
|
+
formatValue(value) {
|
|
125
|
+
if (typeof value === 'object') {
|
|
126
|
+
return JSON.stringify(value, null, 2);
|
|
127
|
+
}
|
|
128
|
+
return String(value);
|
|
129
|
+
}
|
|
27
130
|
constructor(name, subtype, element){
|
|
28
|
-
super(_MetadataTypes.MetaType.VARIABLE, subtype, name, element), _define_property(this, "dataType", void 0);
|
|
131
|
+
super(_MetadataTypes.MetaType.VARIABLE, subtype, name, element), _define_property(this, "dataType", void 0), _define_property(this, "isCollection", void 0), _define_property(this, "isInput", void 0), _define_property(this, "isOutput", void 0), _define_property(this, "objectType", void 0), _define_property(this, "description", void 0), _define_property(this, "value", void 0);
|
|
132
|
+
// Extract properties based on variable subtype
|
|
29
133
|
this.dataType = element["dataType"];
|
|
134
|
+
this.isCollection = element["isCollection"];
|
|
135
|
+
this.isInput = element["isInput"];
|
|
136
|
+
this.isOutput = element["isOutput"];
|
|
137
|
+
this.objectType = element["objectType"];
|
|
138
|
+
this.description = element["description"];
|
|
139
|
+
// Different subtypes have different value properties
|
|
140
|
+
if (subtype === "constants") {
|
|
141
|
+
this.value = element["value"];
|
|
142
|
+
} else if (subtype === "formulas") {
|
|
143
|
+
this.value = element["expression"];
|
|
144
|
+
}
|
|
30
145
|
}
|
|
31
146
|
};
|
|
147
|
+
// Static icon configuration (can be overridden)
|
|
148
|
+
_define_property(FlowVariable, "iconConfig", _VariableIcons.DEFAULT_VARIABLE_ICONS);
|
|
@@ -12,7 +12,7 @@ const _internals = require("../internals/internals");
|
|
|
12
12
|
const _RuleCommon = require("./RuleCommon");
|
|
13
13
|
let LoopRuleCommon = class LoopRuleCommon extends _RuleCommon.RuleCommon {
|
|
14
14
|
check(flow, _options, suppressions) {
|
|
15
|
-
const loopElements =
|
|
15
|
+
const loopElements = flow.graph.getLoopNodes();
|
|
16
16
|
if (!loopElements.length) {
|
|
17
17
|
return [];
|
|
18
18
|
}
|
|
@@ -21,25 +21,24 @@ let LoopRuleCommon = class LoopRuleCommon extends _RuleCommon.RuleCommon {
|
|
|
21
21
|
return results;
|
|
22
22
|
}
|
|
23
23
|
findLoopElements(flow) {
|
|
24
|
-
|
|
25
|
-
return ((_flow_elements = flow.elements) === null || _flow_elements === void 0 ? void 0 : _flow_elements.filter((node)=>node.subtype === "loops")) || [];
|
|
24
|
+
return flow.graph.getLoopNodes();
|
|
26
25
|
}
|
|
27
26
|
findLoopEnd(element) {
|
|
28
|
-
var _element_element_noMoreValuesConnector;
|
|
27
|
+
var _element_element_noMoreValuesConnector, _element_element;
|
|
29
28
|
var _element_element_noMoreValuesConnector_targetReference;
|
|
30
|
-
return (_element_element_noMoreValuesConnector_targetReference = (
|
|
29
|
+
return (_element_element_noMoreValuesConnector_targetReference = (_element_element = element.element) === null || _element_element === void 0 ? void 0 : (_element_element_noMoreValuesConnector = _element_element.noMoreValuesConnector) === null || _element_element_noMoreValuesConnector === void 0 ? void 0 : _element_element_noMoreValuesConnector.targetReference) !== null && _element_element_noMoreValuesConnector_targetReference !== void 0 ? _element_element_noMoreValuesConnector_targetReference : element.name;
|
|
31
30
|
}
|
|
32
31
|
findStatementsInLoops(flow, loopElements) {
|
|
33
32
|
const statementsInLoops = [];
|
|
34
33
|
const statementTypes = this.getStatementTypes();
|
|
35
|
-
const findStatement = (element)=>{
|
|
36
|
-
if (statementTypes.includes(element.subtype)) {
|
|
37
|
-
statementsInLoops.push(element);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
34
|
for (const element of loopElements){
|
|
41
|
-
const
|
|
42
|
-
|
|
35
|
+
const loopElems = flow.graph.getLoopElements(element.name);
|
|
36
|
+
for (const elemName of loopElems){
|
|
37
|
+
const node = flow.graph.getNode(elemName);
|
|
38
|
+
if (node && statementTypes.includes(node.subtype)) {
|
|
39
|
+
statementsInLoops.push(node);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
43
42
|
}
|
|
44
43
|
return statementsInLoops;
|
|
45
44
|
}
|
|
@@ -16,17 +16,40 @@ export declare abstract class RuleCommon {
|
|
|
16
16
|
constructor(info: RuleInfo, optional?: {
|
|
17
17
|
severity?: string;
|
|
18
18
|
});
|
|
19
|
+
execute(flow: core.Flow, options?: object, suppressions?: string[]): core.RuleResult;
|
|
20
|
+
protected abstract check(flow: core.Flow, options: object | undefined, suppressions: Set<string>): core.Violation[];
|
|
21
|
+
protected isSuppressed(name: string, suppressions: Set<string>): boolean;
|
|
19
22
|
/**
|
|
20
|
-
*
|
|
23
|
+
* Get the start node (the special <start> element).
|
|
24
|
+
* This is now stored separately in flow.startNode, not in flow.elements.
|
|
25
|
+
*
|
|
26
|
+
* @param flow - The Flow instance
|
|
27
|
+
* @returns The start FlowNode or undefined if not found
|
|
21
28
|
*/
|
|
22
|
-
|
|
29
|
+
protected getStartNode(flow: core.Flow): core.FlowNode | undefined;
|
|
23
30
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
31
|
+
* Get the reference name of the first actual element (what the flow starts at).
|
|
32
|
+
* This is the element that comes AFTER the start node.
|
|
33
|
+
*
|
|
34
|
+
* @param flow - The Flow instance
|
|
35
|
+
* @returns The start reference name or undefined
|
|
26
36
|
*/
|
|
27
|
-
protected
|
|
37
|
+
protected getStartReference(flow: core.Flow): string | undefined;
|
|
28
38
|
/**
|
|
29
|
-
*
|
|
39
|
+
* Find the INDEX of the first actual element in a FlowNode array.
|
|
40
|
+
* Useful for rules that need to iterate by index.
|
|
41
|
+
*
|
|
42
|
+
* @param flow - The Flow instance
|
|
43
|
+
* @param flowElements - Array of FlowNodes (typically from flow.elements)
|
|
44
|
+
* @returns The index of the starting element, or -1 if not found
|
|
30
45
|
*/
|
|
31
|
-
protected
|
|
46
|
+
protected findStartIndex(flow: core.Flow, flowElements: core.FlowNode[]): number;
|
|
47
|
+
/**
|
|
48
|
+
* Safely get a property from the start element.
|
|
49
|
+
*
|
|
50
|
+
* @param flow - The Flow instance
|
|
51
|
+
* @param propertyName - The property to retrieve (e.g., 'triggerType', 'object')
|
|
52
|
+
* @returns The property value or undefined
|
|
53
|
+
*/
|
|
54
|
+
protected getStartProperty(flow: core.Flow, propertyName: string): any;
|
|
32
55
|
}
|
|
@@ -64,31 +64,69 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
64
64
|
return newObj;
|
|
65
65
|
}
|
|
66
66
|
let RuleCommon = class RuleCommon {
|
|
67
|
-
|
|
68
|
-
* execute() – automatic suppression
|
|
69
|
-
*/ execute(flow, options, suppressions = []) {
|
|
67
|
+
execute(flow, options, suppressions = []) {
|
|
70
68
|
// Wildcard suppression disables entire rule
|
|
71
69
|
if (suppressions.includes("*")) {
|
|
72
70
|
return new _internals.RuleResult(this, []);
|
|
73
71
|
}
|
|
74
|
-
// Convert to Set for fast lookup
|
|
75
72
|
const suppSet = new Set(suppressions);
|
|
76
|
-
// Raw violations from rule
|
|
77
73
|
let violations = this.check(flow, options, suppSet);
|
|
78
|
-
// Automatically filter suppressed violations by their .name
|
|
79
74
|
violations = violations.filter((v)=>!suppSet.has(v.name));
|
|
80
|
-
// Wrap into RuleResult
|
|
81
75
|
return new _internals.RuleResult(this, violations);
|
|
82
76
|
}
|
|
83
|
-
|
|
84
|
-
* Legacy/manual suppression helper (still available for early exits)
|
|
85
|
-
*/ isSuppressed(name, suppressions) {
|
|
77
|
+
isSuppressed(name, suppressions) {
|
|
86
78
|
return suppressions.has(name);
|
|
87
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Get the start node (the special <start> element).
|
|
82
|
+
* This is now stored separately in flow.startNode, not in flow.elements.
|
|
83
|
+
*
|
|
84
|
+
* @param flow - The Flow instance
|
|
85
|
+
* @returns The start FlowNode or undefined if not found
|
|
86
|
+
*/ getStartNode(flow) {
|
|
87
|
+
return flow.startNode;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get the reference name of the first actual element (what the flow starts at).
|
|
91
|
+
* This is the element that comes AFTER the start node.
|
|
92
|
+
*
|
|
93
|
+
* @param flow - The Flow instance
|
|
94
|
+
* @returns The start reference name or undefined
|
|
95
|
+
*/ getStartReference(flow) {
|
|
96
|
+
return flow.startReference || undefined;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Find the INDEX of the first actual element in a FlowNode array.
|
|
100
|
+
* Useful for rules that need to iterate by index.
|
|
101
|
+
*
|
|
102
|
+
* @param flow - The Flow instance
|
|
103
|
+
* @param flowElements - Array of FlowNodes (typically from flow.elements)
|
|
104
|
+
* @returns The index of the starting element, or -1 if not found
|
|
105
|
+
*/ findStartIndex(flow, flowElements) {
|
|
106
|
+
const startRef = this.getStartReference(flow);
|
|
107
|
+
if (!startRef) {
|
|
108
|
+
return -1;
|
|
109
|
+
}
|
|
110
|
+
return flowElements.findIndex((n)=>n.name === startRef);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Safely get a property from the start element.
|
|
114
|
+
*
|
|
115
|
+
* @param flow - The Flow instance
|
|
116
|
+
* @param propertyName - The property to retrieve (e.g., 'triggerType', 'object')
|
|
117
|
+
* @returns The property value or undefined
|
|
118
|
+
*/ getStartProperty(flow, propertyName) {
|
|
119
|
+
var _flow_startNode;
|
|
120
|
+
if ((_flow_startNode = flow.startNode) === null || _flow_startNode === void 0 ? void 0 : _flow_startNode.element) {
|
|
121
|
+
var _flow_startNode_element;
|
|
122
|
+
return (_flow_startNode_element = flow.startNode.element) === null || _flow_startNode_element === void 0 ? void 0 : _flow_startNode_element[propertyName];
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
88
126
|
constructor(info, optional){
|
|
89
127
|
_define_property(this, "description", void 0);
|
|
90
128
|
_define_property(this, "docRefs", []);
|
|
91
|
-
_define_property(this, "isConfigurable", void 0);
|
|
129
|
+
_define_property(this, "isConfigurable", void 0);
|
|
92
130
|
_define_property(this, "label", void 0);
|
|
93
131
|
_define_property(this, "name", void 0);
|
|
94
132
|
_define_property(this, "severity", void 0);
|
package/main/rules/APIVersion.js
CHANGED
|
@@ -65,7 +65,37 @@ let APIVersion = class APIVersion extends _RuleCommon.RuleCommon {
|
|
|
65
65
|
}
|
|
66
66
|
// Custom logic
|
|
67
67
|
if (options === null || options === void 0 ? void 0 : options.expression) {
|
|
68
|
-
|
|
68
|
+
// Match something like: >= 58
|
|
69
|
+
const match = options.expression.match(/^\s*(>=|<=|>|<|===|!==)\s*(\d+)\s*$/);
|
|
70
|
+
if (!match) {
|
|
71
|
+
// Invalid expression format
|
|
72
|
+
return [
|
|
73
|
+
new _internals.Violation(new _internals.FlowAttribute("Invalid API rule expression", "apiVersion", options.expression))
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
const [, operator, versionStr] = match;
|
|
77
|
+
const target = parseFloat(versionStr);
|
|
78
|
+
let isValid = true;
|
|
79
|
+
switch(operator){
|
|
80
|
+
case ">":
|
|
81
|
+
isValid = flowAPIVersionNumber > target;
|
|
82
|
+
break;
|
|
83
|
+
case "<":
|
|
84
|
+
isValid = flowAPIVersionNumber < target;
|
|
85
|
+
break;
|
|
86
|
+
case ">=":
|
|
87
|
+
isValid = flowAPIVersionNumber >= target;
|
|
88
|
+
break;
|
|
89
|
+
case "<=":
|
|
90
|
+
isValid = flowAPIVersionNumber <= target;
|
|
91
|
+
break;
|
|
92
|
+
case "===":
|
|
93
|
+
isValid = flowAPIVersionNumber === target;
|
|
94
|
+
break;
|
|
95
|
+
case "!==":
|
|
96
|
+
isValid = flowAPIVersionNumber !== target;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
69
99
|
if (!isValid) {
|
|
70
100
|
return [
|
|
71
101
|
new _internals.Violation(new _internals.FlowAttribute(`${flowAPIVersionNumber}`, "apiVersion", options.expression))
|
|
@@ -4,6 +4,5 @@ import { IRuleDefinition } from "../interfaces/IRuleDefinition";
|
|
|
4
4
|
export declare class DuplicateDMLOperation extends RuleCommon implements IRuleDefinition {
|
|
5
5
|
constructor();
|
|
6
6
|
protected check(flow: core.Flow, _options: object | undefined, suppressions: Set<string>): core.Violation[];
|
|
7
|
-
private
|
|
8
|
-
private flagDML;
|
|
7
|
+
private isDML;
|
|
9
8
|
}
|