rflib-plugin 0.8.1 → 0.9.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 +10 -1
- package/lib/commands/rflib/logging/flow/instrument.d.ts +2 -1
- package/lib/commands/rflib/logging/flow/instrument.js +121 -62
- package/lib/commands/rflib/logging/flow/instrument.js.map +1 -1
- package/messages/rflib.logging.flow.instrument.md +2 -2
- package/oclif.lock +696 -326
- package/oclif.manifest.json +3 -3
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ sf rflib logging aura instrument --sourcepath force-app --skip-instrumented
|
|
|
96
96
|
|
|
97
97
|
### `sf rflib logging flow instrument`
|
|
98
98
|
|
|
99
|
-
Adds RFLIB logging actions to Salesforce Flows.
|
|
99
|
+
Adds RFLIB logging actions to Salesforce Flows and optimizes flow layout.
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
102
|
# Add logging to all Flow files
|
|
@@ -115,6 +115,15 @@ sf rflib logging flow instrument --sourcepath force-app --skip-instrumented
|
|
|
115
115
|
- `--dryrun (-d)`: Preview changes without modifying files
|
|
116
116
|
- `--skip-instrumented`: Do not instrument files where RFLIB logging is already present
|
|
117
117
|
|
|
118
|
+
#### Features
|
|
119
|
+
|
|
120
|
+
- Adds logging for flow invocation at the start of the flow
|
|
121
|
+
- Adds logging for decision paths to track which branch is executed
|
|
122
|
+
- Sets the flow's CanvasMode to AUTO_LAYOUT_CANVAS for better visualization in Flow Builder
|
|
123
|
+
- Preserves the original processType value
|
|
124
|
+
- Handles both free-form and auto-layout flows, converting all to auto-layout
|
|
125
|
+
- Supports both standard Flows (processType="Flow") and Auto-Launched Flows (processType="AutoLaunchedFlow")
|
|
126
|
+
|
|
118
127
|
## Contributing
|
|
119
128
|
|
|
120
129
|
1. Fork the repository
|
|
@@ -9,7 +9,7 @@ export declare class FlowInstrumentationService {
|
|
|
9
9
|
static parseFlowContent(content: string): Promise<any>;
|
|
10
10
|
static buildFlowContent(flowObj: any): string;
|
|
11
11
|
static hasRFLIBLogger(flowObj: any): boolean;
|
|
12
|
-
static
|
|
12
|
+
static isSupportedProcessType(flowObj: any): boolean;
|
|
13
13
|
static instrumentFlow(flowObj: any, flowName: string, skipInstrumented?: boolean): any;
|
|
14
14
|
private static instrumentDecisions;
|
|
15
15
|
private static addActionCallToFlow;
|
|
@@ -18,6 +18,7 @@ export declare class FlowInstrumentationService {
|
|
|
18
18
|
private static sanitizeForName;
|
|
19
19
|
private static truncateLabel;
|
|
20
20
|
private static createLoggingAction;
|
|
21
|
+
private static setCanvasMode;
|
|
21
22
|
private static enhanceLoggingWithVariables;
|
|
22
23
|
}
|
|
23
24
|
export default class RflibLoggingFlowInstrument extends SfCommand<RflibLoggingFlowInstrumentResult> {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable no-await-in-loop */
|
|
1
|
+
/* eslint-disable no-await-in-loop, sf-plugin/only-extend-SfCommand */
|
|
2
2
|
/* eslint-disable @typescript-eslint/return-await */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
@@ -7,13 +7,11 @@
|
|
|
7
7
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
8
8
|
import * as fs from 'node:fs';
|
|
9
9
|
import * as path from 'node:path';
|
|
10
|
-
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
11
10
|
import { Messages, Logger } from '@salesforce/core';
|
|
11
|
+
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
12
12
|
import * as xml2js from 'xml2js';
|
|
13
13
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
14
14
|
const messages = Messages.loadMessages('rflib-plugin', 'rflib.logging.flow.instrument');
|
|
15
|
-
// Type definition removed to fix compiler error
|
|
16
|
-
// This was used to document valid Flow variable types: 'String' | 'Number' | 'Boolean' | 'SObject' | 'SObjectCollection'
|
|
17
15
|
export class FlowInstrumentationService {
|
|
18
16
|
static parser = new xml2js.Parser({
|
|
19
17
|
explicitArray: false,
|
|
@@ -22,7 +20,12 @@ export class FlowInstrumentationService {
|
|
|
22
20
|
});
|
|
23
21
|
static builder = new xml2js.Builder({
|
|
24
22
|
xmldec: { version: '1.0', encoding: 'UTF-8' },
|
|
25
|
-
cdata: true
|
|
23
|
+
cdata: true,
|
|
24
|
+
renderOpts: {
|
|
25
|
+
pretty: true,
|
|
26
|
+
indent: ' ',
|
|
27
|
+
newline: '\n'
|
|
28
|
+
}
|
|
26
29
|
});
|
|
27
30
|
static async parseFlowContent(content) {
|
|
28
31
|
try {
|
|
@@ -37,7 +40,25 @@ export class FlowInstrumentationService {
|
|
|
37
40
|
}
|
|
38
41
|
static buildFlowContent(flowObj) {
|
|
39
42
|
try {
|
|
40
|
-
|
|
43
|
+
if (!flowObj?.Flow) {
|
|
44
|
+
throw new Error('Invalid flow object structure');
|
|
45
|
+
}
|
|
46
|
+
// Create a new Flow object with ordered properties
|
|
47
|
+
const orderedFlow = {
|
|
48
|
+
'$': { 'xmlns': 'http://soap.sforce.com/2006/04/metadata' }
|
|
49
|
+
};
|
|
50
|
+
// Add actionCalls first if it exists
|
|
51
|
+
if (flowObj.Flow.actionCalls) {
|
|
52
|
+
orderedFlow.actionCalls = flowObj.Flow.actionCalls;
|
|
53
|
+
}
|
|
54
|
+
// Add all other properties in their original order
|
|
55
|
+
Object.entries(flowObj.Flow).forEach(([key, value]) => {
|
|
56
|
+
if (key !== 'actionCalls' && key !== '$') {
|
|
57
|
+
orderedFlow[key] = value;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// Use the builder with just the Flow object, not wrapped in another object
|
|
61
|
+
return this.builder.buildObject({ Flow: orderedFlow });
|
|
41
62
|
}
|
|
42
63
|
catch (error) {
|
|
43
64
|
if (error instanceof Error) {
|
|
@@ -59,9 +80,12 @@ export class FlowInstrumentationService {
|
|
|
59
80
|
action.actionName === 'rflib_ApplicationEventLoggerAction' ||
|
|
60
81
|
(action.name && typeof action.name === 'string' && action.name.startsWith('RFLIB_Flow_Logger')));
|
|
61
82
|
}
|
|
62
|
-
// Helper to check if flow
|
|
63
|
-
static
|
|
64
|
-
|
|
83
|
+
// Helper to check if flow has a supported process type for instrumentation
|
|
84
|
+
static isSupportedProcessType(flowObj) {
|
|
85
|
+
const processType = flowObj?.Flow?.processType;
|
|
86
|
+
const triggerType = flowObj?.Flow?.start?.triggerType;
|
|
87
|
+
return processType === 'Flow' ||
|
|
88
|
+
(processType === 'AutoLaunchedFlow' && triggerType === 'RecordAfterSave');
|
|
65
89
|
}
|
|
66
90
|
// Main instrumentation function
|
|
67
91
|
static instrumentFlow(flowObj, flowName, skipInstrumented = false) {
|
|
@@ -84,13 +108,12 @@ export class FlowInstrumentationService {
|
|
|
84
108
|
instrumentedFlow.Flow.actionCalls = loggingAction;
|
|
85
109
|
}
|
|
86
110
|
else if (Array.isArray(instrumentedFlow.Flow.actionCalls)) {
|
|
87
|
-
instrumentedFlow.Flow.actionCalls.
|
|
111
|
+
instrumentedFlow.Flow.actionCalls.unshift(loggingAction);
|
|
88
112
|
}
|
|
89
113
|
else {
|
|
90
|
-
|
|
91
|
-
instrumentedFlow.Flow.actionCalls = [instrumentedFlow.Flow.actionCalls, loggingAction];
|
|
114
|
+
instrumentedFlow.Flow.actionCalls = [loggingAction, instrumentedFlow.Flow.actionCalls];
|
|
92
115
|
}
|
|
93
|
-
// Find startElementReference and connect logger to it
|
|
116
|
+
// Find startElementReference or start element and connect logger to it
|
|
94
117
|
if (instrumentedFlow.Flow.startElementReference) {
|
|
95
118
|
// Save the original start reference
|
|
96
119
|
const startNodeReference = instrumentedFlow.Flow.startElementReference;
|
|
@@ -101,43 +124,37 @@ export class FlowInstrumentationService {
|
|
|
101
124
|
// Update flow startElementReference to point to our logger
|
|
102
125
|
instrumentedFlow.Flow.startElementReference = loggingAction.name;
|
|
103
126
|
}
|
|
104
|
-
else {
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// Find the first screen and connect to it
|
|
116
|
-
const firstScreen = instrumentedFlow.Flow.screens[0];
|
|
117
|
-
loggingAction.connector = {
|
|
118
|
-
targetReference: firstScreen.name
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
// Create a startElementReference pointing to our logger if none exists
|
|
127
|
+
else if (instrumentedFlow.Flow.start?.connector?.targetReference) {
|
|
128
|
+
// Handle flow with start element: create connector and update start reference
|
|
129
|
+
const startElement = instrumentedFlow.Flow.start;
|
|
130
|
+
const originalTarget = startElement.connector.targetReference;
|
|
131
|
+
// Create connector between logger and the original target
|
|
132
|
+
loggingAction.connector = {
|
|
133
|
+
targetReference: originalTarget
|
|
134
|
+
};
|
|
135
|
+
// Update the start element connector to point to our logger
|
|
136
|
+
startElement.connector.targetReference = loggingAction.name;
|
|
137
|
+
// Also set the startElementReference for consistency
|
|
122
138
|
instrumentedFlow.Flow.startElementReference = loggingAction.name;
|
|
123
139
|
}
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
instrumentedFlow.Flow.interviewLabel = `${flowName} {!$Flow.CurrentDateTime}`;
|
|
127
|
-
}
|
|
128
|
-
// Ensure processType is set to 'Flow'
|
|
129
|
-
instrumentedFlow.Flow.processType = 'Flow';
|
|
130
|
-
// Add variables if not present (needed for variable references)
|
|
131
|
-
if (!instrumentedFlow.Flow.variables) {
|
|
132
|
-
instrumentedFlow.Flow.variables = [];
|
|
133
|
-
}
|
|
134
|
-
else if (!Array.isArray(instrumentedFlow.Flow.variables)) {
|
|
135
|
-
instrumentedFlow.Flow.variables = [instrumentedFlow.Flow.variables];
|
|
136
|
-
}
|
|
140
|
+
// Set the CanvasMode to AUTO_LAYOUT_CANVAS
|
|
141
|
+
this.setCanvasMode(instrumentedFlow);
|
|
137
142
|
// Instrument decisions with logging for each outcome
|
|
138
143
|
if (instrumentedFlow.Flow.decisions) {
|
|
139
144
|
this.instrumentDecisions(instrumentedFlow, flowName);
|
|
140
145
|
}
|
|
146
|
+
// Reorder Flow properties
|
|
147
|
+
const originalFlow = instrumentedFlow.Flow;
|
|
148
|
+
const orderedFlow = {
|
|
149
|
+
$: originalFlow.$,
|
|
150
|
+
actionCalls: originalFlow.actionCalls
|
|
151
|
+
};
|
|
152
|
+
Object.keys(originalFlow).forEach(key => {
|
|
153
|
+
if (key !== 'actionCalls' && key !== '$') {
|
|
154
|
+
orderedFlow[key] = originalFlow[key];
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
instrumentedFlow.Flow = orderedFlow;
|
|
141
158
|
return instrumentedFlow;
|
|
142
159
|
}
|
|
143
160
|
// Helper to instrument decision paths with logging
|
|
@@ -151,11 +168,12 @@ export class FlowInstrumentationService {
|
|
|
151
168
|
: [flowObj.Flow.decisions];
|
|
152
169
|
// Process each decision
|
|
153
170
|
decisions.forEach((decision) => {
|
|
154
|
-
//
|
|
155
|
-
|
|
171
|
+
// Support decision name as 'name' or legacy 'n'
|
|
172
|
+
const decisionNameRaw = decision.name ?? decision.n;
|
|
173
|
+
if (!decisionNameRaw) {
|
|
156
174
|
return;
|
|
157
175
|
}
|
|
158
|
-
const decisionName =
|
|
176
|
+
const decisionName = decisionNameRaw;
|
|
159
177
|
const decisionLabel = decision.label || decisionName;
|
|
160
178
|
// Process default connector if it exists
|
|
161
179
|
if (decision.defaultConnector?.targetReference) {
|
|
@@ -179,12 +197,13 @@ export class FlowInstrumentationService {
|
|
|
179
197
|
if (decision.rules) {
|
|
180
198
|
const rules = Array.isArray(decision.rules) ? decision.rules : [decision.rules];
|
|
181
199
|
rules.forEach((rule) => {
|
|
182
|
-
// Skip if rule doesn't have a connector or name
|
|
183
|
-
|
|
200
|
+
// Skip if rule doesn't have a connector or name (support 'name' or legacy 'n')
|
|
201
|
+
const ruleNameRaw = rule.name ?? rule.n;
|
|
202
|
+
if (!rule.connector?.targetReference || !ruleNameRaw) {
|
|
184
203
|
return;
|
|
185
204
|
}
|
|
186
205
|
const ruleTarget = rule.connector.targetReference;
|
|
187
|
-
const ruleName =
|
|
206
|
+
const ruleName = ruleNameRaw;
|
|
188
207
|
const ruleLabel = rule.label || ruleName;
|
|
189
208
|
// Create a logger for this rule outcome
|
|
190
209
|
const ruleLogger = this.createDecisionPathLogger(flowName, String(decisionName), String(decisionLabel), String(ruleName), String(ruleLabel));
|
|
@@ -212,11 +231,12 @@ export class FlowInstrumentationService {
|
|
|
212
231
|
flowObj.Flow.actionCalls = actionCall;
|
|
213
232
|
}
|
|
214
233
|
else if (Array.isArray(flowObj.Flow.actionCalls)) {
|
|
215
|
-
|
|
234
|
+
// Add new action at the beginning of the array
|
|
235
|
+
flowObj.Flow.actionCalls.unshift(actionCall);
|
|
216
236
|
}
|
|
217
237
|
else {
|
|
218
|
-
// If only one action exists, convert to array
|
|
219
|
-
flowObj.Flow.actionCalls = [flowObj.Flow.actionCalls
|
|
238
|
+
// If only one action exists, convert to array with new action first
|
|
239
|
+
flowObj.Flow.actionCalls = [actionCall, flowObj.Flow.actionCalls];
|
|
220
240
|
}
|
|
221
241
|
/* eslint-enable no-param-reassign */
|
|
222
242
|
}
|
|
@@ -358,12 +378,8 @@ export class FlowInstrumentationService {
|
|
|
358
378
|
// Helper to create a logging action element
|
|
359
379
|
static createLoggingAction(flowName) {
|
|
360
380
|
const loggerId = this.generateUniqueId();
|
|
361
|
-
//
|
|
362
|
-
const
|
|
363
|
-
const maxFlowNameLength = 80 - prefixLength - loggerId.length - 1; // -1 for underscore
|
|
364
|
-
const sanitizedFlowName = this.sanitizeForName(flowName, maxFlowNameLength);
|
|
365
|
-
// Create a name that's guaranteed to be under 80 chars and follow Salesforce rules
|
|
366
|
-
const name = `RFLIB_Flow_Logger_${sanitizedFlowName}_${loggerId}`;
|
|
381
|
+
// Create a name for the flow invocation logger (omit flowName to avoid conflicts)
|
|
382
|
+
const name = `RFLIB_Flow_Logger_${loggerId}`;
|
|
367
383
|
// Create and truncate the label to ensure it's under 80 chars
|
|
368
384
|
const label = this.truncateLabel(`Log Flow Invocation: ${flowName}`);
|
|
369
385
|
// Verify name length
|
|
@@ -427,6 +443,49 @@ export class FlowInstrumentationService {
|
|
|
427
443
|
],
|
|
428
444
|
};
|
|
429
445
|
}
|
|
446
|
+
// Helper to set CanvasMode to AUTO_LAYOUT_CANVAS for better flow layout
|
|
447
|
+
static setCanvasMode(flowObj) {
|
|
448
|
+
// No longer automatically setting canvas mode - preserve original mode
|
|
449
|
+
if (!flowObj.Flow) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
// Preserve original processMetadataValues state
|
|
453
|
+
const originalMeta = flowObj.Flow.processMetadataValues;
|
|
454
|
+
const hadProcessMetadataValues = !!originalMeta;
|
|
455
|
+
// Normalize to array
|
|
456
|
+
const metadataValues = !originalMeta
|
|
457
|
+
? []
|
|
458
|
+
: Array.isArray(originalMeta)
|
|
459
|
+
? originalMeta
|
|
460
|
+
: [originalMeta];
|
|
461
|
+
// Prepare CanvasMode entry
|
|
462
|
+
const canvasModeEntry = {
|
|
463
|
+
name: 'CanvasMode',
|
|
464
|
+
value: {
|
|
465
|
+
stringValue: 'AUTO_LAYOUT_CANVAS'
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
// Check if CanvasMode metadata exists
|
|
469
|
+
const canvasModeIndex = metadataValues.findIndex((meta) => meta.name === 'CanvasMode');
|
|
470
|
+
if (canvasModeIndex === -1) {
|
|
471
|
+
// Add AUTO_LAYOUT_CANVAS entry
|
|
472
|
+
metadataValues.push(canvasModeEntry);
|
|
473
|
+
// Duplicate entry for flows that had no metadata to ensure array output on single entry
|
|
474
|
+
if (!hadProcessMetadataValues) {
|
|
475
|
+
metadataValues.push({
|
|
476
|
+
name: canvasModeEntry.name,
|
|
477
|
+
value: { stringValue: canvasModeEntry.value.stringValue }
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
metadataValues[canvasModeIndex].value.stringValue = 'AUTO_LAYOUT_CANVAS';
|
|
483
|
+
}
|
|
484
|
+
// Assign back the potentially modified array
|
|
485
|
+
/* eslint-disable no-param-reassign */
|
|
486
|
+
flowObj.Flow.processMetadataValues = metadataValues;
|
|
487
|
+
/* eslint-enable no-param-reassign */
|
|
488
|
+
}
|
|
430
489
|
// Helper to add variable references to the logging message when available
|
|
431
490
|
static enhanceLoggingWithVariables(loggingAction, flowObj) {
|
|
432
491
|
// Find input variables or parameters that might be useful to log
|
|
@@ -519,9 +578,9 @@ export default class RflibLoggingFlowInstrument extends SfCommand {
|
|
|
519
578
|
this.stats.processedFiles++;
|
|
520
579
|
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
521
580
|
const flowObj = await FlowInstrumentationService.parseFlowContent(content);
|
|
522
|
-
// Only instrument flows with
|
|
523
|
-
if (!FlowInstrumentationService.
|
|
524
|
-
this.logger.debug(`Skipping
|
|
581
|
+
// Only instrument flows with supported process types
|
|
582
|
+
if (!FlowInstrumentationService.isSupportedProcessType(flowObj)) {
|
|
583
|
+
this.logger.debug(`Skipping unsupported flow type: ${flowName} (processType=${flowObj?.Flow?.processType || 'undefined'})`);
|
|
525
584
|
return;
|
|
526
585
|
}
|
|
527
586
|
// Check if flow already has RFLIB logging and skip if needed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument.js","sourceRoot":"","sources":["../../../../../src/commands/rflib/logging/flow/instrument.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,oDAAoD;AACpD,uDAAuD;AACvD,sDAAsD;AACtD,4DAA4D;AAC5D,wDAAwD;AACxD,+DAA+D;AAC/D,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAOjC,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,cAAc,EAAE,+BAA+B,CAAC,CAAC;AAExF,gDAAgD;AAChD,yHAAyH;AAEzH,MAAM,OAAO,0BAA0B;IAC7B,MAAM,CAAU,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC;QACjD,aAAa,EAAE,KAAK;QACpB,qBAAqB,EAAE,IAAI;QAC3B,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IAEK,MAAM,CAAU,OAAO,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC;QACnD,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE;QAC7C,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IAEI,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAe;QAClD,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC3B,MAAM,IAAI,KAAK,CAAC,wBAAwB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3D,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAEM,MAAM,CAAC,gBAAgB,CAAC,OAAY;QACzC,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC3B,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,wDAAwD;IACjD,MAAM,CAAC,cAAc,CAAC,OAAY;QACvC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC;YACzD,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW;YAC1B,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE/B,OAAO,WAAW,CAAC,IAAI,CACrB,CAAC,MAAW,EAAE,EAAE,CACd,MAAM,CAAC,UAAU,KAAK,cAAc;YACpC,MAAM,CAAC,UAAU,KAAK,wBAAwB;YAC9C,MAAM,CAAC,UAAU,KAAK,oCAAoC;YAC1D,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAClG,CAAC;IACJ,CAAC;IAED,gEAAgE;IACzD,MAAM,CAAC,UAAU,CAAC,OAAY;QACnC,OAAO,OAAO,EAAE,IAAI,EAAE,WAAW,KAAK,MAAM,CAAC;IAC/C,CAAC;IAED,gCAAgC;IACzB,MAAM,CAAC,cAAc,CAAC,OAAY,EAAE,QAAgB,EAAE,gBAAgB,GAAG,KAAK;QACnF,wDAAwD;QACxD,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QAE7D,gEAAgE;QAChE,IAAI,gBAAgB,IAAI,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC9D,OAAO,gBAAgB,CAAC;QAC1B,CAAC;QAED,sCAAsC;QACtC,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;YAC3B,OAAO,gBAAgB,CAAC;QAC1B,CAAC;QAED,gCAAgC;QAChC,IAAI,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QAEvD,oDAAoD;QACpD,aAAa,GAAG,IAAI,CAAC,2BAA2B,CAAC,aAAa,EAAE,gBAAgB,CAAC,CAAC;QAElF,oCAAoC;QACpC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACvC,gBAAgB,CAAC,IAAI,CAAC,WAAW,GAAG,aAAa,CAAC;QACpD,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5D,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,8CAA8C;YAC9C,gBAAgB,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;QACzF,CAAC;QAED,sDAAsD;QACtD,IAAI,gBAAgB,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAChD,oCAAoC;YACpC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC,IAAI,CAAC,qBAAqB,CAAC;YAEvE,6DAA6D;YAC7D,aAAa,CAAC,SAAS,GAAG;gBACxB,eAAe,EAAE,kBAAkB;aACpC,CAAC;YAEF,2DAA2D;YAC3D,gBAAgB,CAAC,IAAI,CAAC,qBAAqB,GAAG,aAAa,CAAC,IAAI,CAAC;QACnE,CAAC;aAAM,CAAC;YACN,uDAAuD;YACvD,uEAAuE;YACvE,IAAI,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACjG,4CAA4C;gBAC5C,MAAM,aAAa,GAAG,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBACzD,aAAa,CAAC,SAAS,GAAG;oBACxB,eAAe,EAAE,aAAa,CAAC,IAAI;iBACpC,CAAC;YACJ,CAAC;iBAAM,IAAI,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpG,0CAA0C;gBAC1C,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBACrD,aAAa,CAAC,SAAS,GAAG;oBACxB,eAAe,EAAE,WAAW,CAAC,IAAI;iBAClC,CAAC;YACJ,CAAC;YAED,uEAAuE;YACvE,gBAAgB,CAAC,IAAI,CAAC,qBAAqB,GAAG,aAAa,CAAC,IAAI,CAAC;QACnE,CAAC;QAED,oCAAoC;QACpC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1C,gBAAgB,CAAC,IAAI,CAAC,cAAc,GAAG,GAAG,QAAQ,2BAA2B,CAAC;QAChF,CAAC;QAED,sCAAsC;QACtC,gBAAgB,CAAC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;QAE3C,gEAAgE;QAChE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACrC,gBAAgB,CAAC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QACvC,CAAC;aAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3D,gBAAgB,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtE,CAAC;QAED,qDAAqD;QACrD,IAAI,gBAAgB,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpC,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,mDAAmD;IAC3C,MAAM,CAAC,mBAAmB,CAAC,OAAY,EAAE,QAAgB;QAC/D,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QAED,gDAAgD;QAChD,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;YACrD,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS;YACxB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAE7B,wBAAwB;QACxB,SAAS,CAAC,OAAO,CAAC,CAAC,QAAa,EAAE,EAAE;YAClC,uCAAuC;YACvC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACnB,OAAO;YACT,CAAC;YAED,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC;YACnC,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,IAAI,YAAY,CAAC;YAErD,yCAAyC;YACzC,IAAI,QAAQ,CAAC,gBAAgB,EAAE,eAAe,EAAE,CAAC;gBAC/C,MAAM,aAAa,GAAG,QAAQ,CAAC,gBAAgB,CAAC,eAAe,CAAC;gBAChE,MAAM,qBAAqB,GAAG,QAAQ,CAAC,qBAAqB,IAAI,iBAAiB,CAAC;gBAElF,uCAAuC;gBACvC,MAAM,aAAa,GAAG,IAAI,CAAC,wBAAwB,CACjD,QAAQ,EACR,MAAM,CAAC,YAAY,CAAC,EACpB,MAAM,CAAC,aAAa,CAAC,EACrB,SAAS,EACT,MAAM,CAAC,qBAAqB,CAAC,CAC9B,CAAC;gBAEF,wCAAwC;gBACxC,aAAa,CAAC,SAAS,GAAG;oBACxB,eAAe,EAAE,aAAa;iBAC/B,CAAC;gBAEF,0EAA0E;gBAC1E,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;gBAEjD,iEAAiE;gBACjE,4EAA4E;gBAC5E,sCAAsC;gBACtC,QAAQ,CAAC,gBAAgB,CAAC,eAAe,GAAG,aAAa,CAAC,IAAI,CAAC;gBAC/D,qCAAqC;YACvC,CAAC;YAED,kCAAkC;YAClC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACnB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAEhF,KAAK,CAAC,OAAO,CAAC,CAAC,IAAS,EAAE,EAAE;oBAC1B,gDAAgD;oBAChD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;wBACnD,OAAO;oBACT,CAAC;oBAED,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC;oBAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;oBAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,QAAQ,CAAC;oBAEzC,wCAAwC;oBACxC,MAAM,UAAU,GAAG,IAAI,CAAC,wBAAwB,CAC9C,QAAQ,EACR,MAAM,CAAC,YAAY,CAAC,EACpB,MAAM,CAAC,aAAa,CAAC,EACrB,MAAM,CAAC,QAAQ,CAAC,EAChB,MAAM,CAAC,SAAS,CAAC,CAClB,CAAC;oBAEF,wCAAwC;oBACxC,UAAU,CAAC,SAAS,GAAG;wBACrB,eAAe,EAAE,UAAU;qBAC5B,CAAC;oBAEF,sEAAsE;oBACtE,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;oBAE9C,qDAAqD;oBACrD,4EAA4E;oBAC5E,sCAAsC;oBACtC,IAAI,CAAC,SAAS,CAAC,eAAe,GAAG,UAAU,CAAC,IAAI,CAAC;oBACjD,qCAAqC;gBACvC,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gDAAgD;IAChD,wFAAwF;IACxF,+GAA+G;IACvG,MAAM,CAAC,mBAAmB,CAAC,OAAY,EAAE,UAAe;QAC9D,sCAAsC;QACtC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QACxC,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,8CAA8C;YAC9C,OAAO,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACpE,CAAC;QACD,qCAAqC;IACvC,CAAC;IAED,uDAAuD;IAC/C,MAAM,CAAC,wBAAwB,CACrC,QAAgB,EAChB,YAAoB,EACpB,aAAqB,EACrB,WAAmB,EACnB,YAAoB;QAEpB,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAEzC,oCAAoC;QACpC,MAAM,eAAe,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;QAC7C,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QAC3C,MAAM,gBAAgB,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;QAC/C,MAAM,eAAe,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;QAE7C,wDAAwD;QACxD,MAAM,YAAY,GAAG,6BAA6B,CAAC,MAAM,CAAC;QAC1D,MAAM,gBAAgB,GAAG,CAAC,CAAC,CAAC,kBAAkB;QAC9C,MAAM,kBAAkB,GAAG,EAAE,GAAG,YAAY,GAAG,QAAQ,CAAC,MAAM,GAAG,gBAAgB,CAAC;QAElF,uEAAuE;QACvE,MAAM,mBAAmB,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,GAAG,CAAC,CAAC,CAAC;QAE/D,gDAAgD;QAChD,MAAM,qBAAqB,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC;QACzF,MAAM,oBAAoB,GAAG,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC;QAEvF,mFAAmF;QACnF,MAAM,IAAI,GAAG,8BAA8B,qBAAqB,IAAI,oBAAoB,IAAI,QAAQ,EAAE,CAAC;QAEvG,8DAA8D;QAC9D,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,gBAAgB,MAAM,eAAe,EAAE,CAAC,CAAC;QAE3F,6BAA6B;QAC7B,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACrB,OAAO;gBACL,UAAU,EAAE,wBAAwB;gBACpC,UAAU,EAAE,MAAM;gBAClB,IAAI,EAAE,cAAc,QAAQ,EAAE;gBAC9B,KAAK;gBACL,SAAS,EAAE,GAAG;gBACd,SAAS,EAAE,EAAE;gBACb,eAAe,EAAE;oBACf;wBACE,IAAI,EAAE,SAAS;wBACf,KAAK,EAAE;4BACL,WAAW,EAAE,QAAQ;yBACtB;qBACF;oBACD;wBACE,IAAI,EAAE,UAAU;wBAChB,KAAK,EAAE;4BACL,WAAW,EAAE,MAAM;yBACpB;qBACF;oBACD;wBACE,IAAI,EAAE,SAAS;wBACf,KAAK,EAAE;4BACL,WAAW,EAAE,aAAa,gBAAgB,cAAc,eAAe,EAAE;yBAC1E;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QAED,OAAO;YACL,UAAU,EAAE,wBAAwB;YACpC,UAAU,EAAE,MAAM;YAClB,IAAI;YACJ,KAAK;YACL,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,EAAE;YACb,eAAe,EAAE;gBACf;oBACE,IAAI,EAAE,SAAS;oBACf,KAAK,EAAE;wBACL,WAAW,EAAE,QAAQ;qBACtB;iBACF;gBACD;oBACE,IAAI,EAAE,UAAU;oBAChB,KAAK,EAAE;wBACL,WAAW,EAAE,MAAM;qBACpB;iBACF;gBACD;oBACE,IAAI,EAAE,SAAS;oBACf,KAAK,EAAE;wBACL,WAAW,EAAE,aAAa,gBAAgB,cAAc,eAAe,EAAE;qBAC1E;iBACF;aACF;SACF,CAAC;IACJ,CAAC;IAED,kFAAkF;IAClF,kDAAkD;IAC1C,MAAM,CAAC,gBAAgB;QAC7B,uEAAuE;QACvE,+EAA+E;QAC/E,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAE1D,8EAA8E;QAC9E,OAAO,KAAK,SAAS,GAAG,MAAM,EAAE,CAAC;IACnC,CAAC;IAED,4EAA4E;IAC5E,qDAAqD;IACrD,iDAAiD;IACjD,6BAA6B;IAC7B,cAAc;IACd,6BAA6B;IAC7B,+BAA+B;IACvB,MAAM,CAAC,eAAe,CAAC,IAAY,EAAE,SAAiB;QAC5D,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,GAAG,CAAC,CAAC,mEAAmE;QACjF,CAAC;QAED,kEAAkE;QAClE,IAAI,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QAEpD,iCAAiC;QACjC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACjC,SAAS,GAAG,GAAG,GAAG,SAAS,CAAC;QAC9B,CAAC;QAED,2DAA2D;QAC3D,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE3C,wCAAwC;QACxC,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAEzC,sDAAsD;QACtD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,GAAG,CAAC;QACb,CAAC;QAED,oCAAoC;QACpC,IAAI,SAAS,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;YACjC,wDAAwD;YACxD,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAEjE,qFAAqF;YACrF,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,SAAS,GAAG,GAAG,CAAC;YAClB,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,gEAAgE;IACxD,MAAM,CAAC,aAAa,CAAC,KAAa,EAAE,YAAoB,EAAE;QAChE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;YACxC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,oDAAoD;QACpD,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;IACnD,CAAC;IAED,4CAA4C;IACpC,MAAM,CAAC,mBAAmB,CAAC,QAAgB;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAEzC,yEAAyE;QACzE,MAAM,YAAY,GAAG,oBAAoB,CAAC,MAAM,CAAC;QACjD,MAAM,iBAAiB,GAAG,EAAE,GAAG,YAAY,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,oBAAoB;QACvF,MAAM,iBAAiB,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAE5E,mFAAmF;QACnF,MAAM,IAAI,GAAG,qBAAqB,iBAAiB,IAAI,QAAQ,EAAE,CAAC;QAElE,8DAA8D;QAC9D,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,wBAAwB,QAAQ,EAAE,CAAC,CAAC;QAErE,qBAAqB;QACrB,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACrB,4DAA4D;YAC5D,OAAO;gBACL,UAAU,EAAE,wBAAwB;gBACpC,UAAU,EAAE,MAAM;gBAClB,IAAI,EAAE,cAAc,QAAQ,EAAE;gBAC9B,KAAK;gBACL,SAAS,EAAE,GAAG;gBACd,SAAS,EAAE,EAAE;gBACb,eAAe,EAAE;oBACf;wBACE,IAAI,EAAE,SAAS;wBACf,KAAK,EAAE;4BACL,WAAW,EAAE,QAAQ;yBACtB;qBACF;oBACD;wBACE,IAAI,EAAE,UAAU;wBAChB,KAAK,EAAE;4BACL,WAAW,EAAE,MAAM;yBACpB;qBACF;oBACD;wBACE,IAAI,EAAE,SAAS;wBACf,KAAK,EAAE;4BACL,WAAW,EAAE,QAAQ,QAAQ,UAAU;yBACxC;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QAED,OAAO;YACL,UAAU,EAAE,wBAAwB;YACpC,UAAU,EAAE,MAAM;YAClB,IAAI;YACJ,KAAK;YACL,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,EAAE;YACb,eAAe,EAAE;gBACf;oBACE,IAAI,EAAE,SAAS;oBACf,KAAK,EAAE;wBACL,WAAW,EAAE,QAAQ;qBACtB;iBACF;gBACD;oBACE,IAAI,EAAE,UAAU;oBAChB,KAAK,EAAE;wBACL,WAAW,EAAE,MAAM;qBACpB;iBACF;gBACD;oBACE,IAAI,EAAE,SAAS;oBACf,KAAK,EAAE;wBACL,WAAW,EAAE,QAAQ,QAAQ,UAAU;qBACxC;iBACF;aACF;SACF,CAAC;IACJ,CAAC;IAED,0EAA0E;IAClE,MAAM,CAAC,2BAA2B,CAAC,aAAkB,EAAE,OAAY;QACzE,iEAAiE;QACjE,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC;QAC/C,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;YAC7C,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,YAAY,KAAK,MAAM,CAAC;YACjF,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,KAAK,MAAM,IAAI,SAAS,CAAC,YAAY,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAE3F,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,uDAAuD;YACvD,MAAM,iBAAiB,GAAG,aAAa,CAAC,eAAe,CAAC,SAAS,CAC/D,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,SAAS,CAChD,CAAC;YAEF,IAAI,iBAAiB,IAAI,CAAC,EAAE,CAAC;gBAC3B,gDAAgD;gBAChD,MAAM,OAAO,GAAG,cAAc;qBAC3B,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC;qBAC1C,IAAI,CAAC,IAAI,CAAC,CAAC;gBAEd,MAAM,WAAW,GAAG,aAAa,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;gBACrE,MAAM,eAAe,GAAG,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC;gBACtD,WAAW,CAAC,KAAK,CAAC,WAAW,GAAG,GAAG,eAAe,SAAS,OAAO,EAAE,CAAC;YACvE,CAAC;QACH,CAAC;QAED,OAAO,aAAa,CAAC;IACvB,CAAC;;AAGH,MAAM,CAAC,OAAO,OAAO,0BAA2B,SAAQ,SAA2C;IAC1F,MAAM,CAAU,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,CAAU,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACjE,MAAM,CAAU,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAE5D,MAAM,CAAU,KAAK,GAAG;QAC7B,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC;YACvB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,0BAA0B,CAAC;YACxD,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,8BAA8B,CAAC;YAChE,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,IAAI;SACf,CAAC;QACF,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC;YACpB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,sBAAsB,CAAC;YACpD,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,0BAA0B,CAAC;YAC5D,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,KAAK;SACf,CAAC;QACF,mBAAmB,EAAE,KAAK,CAAC,OAAO,CAAC;YACjC,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,iCAAiC,CAAC,IAAI,4CAA4C;YAC/G,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,qCAAqC,CAAC,IAAI,gEAAgE;YAC3I,OAAO,EAAE,KAAK;SACf,CAAC;KACH,CAAC;IAEM,MAAM,CAAU;IACP,KAAK,GAAqC;QACzD,cAAc,EAAE,CAAC;QACjB,aAAa,EAAE,CAAC;KACjB,CAAC;IAEK,KAAK,CAAC,GAAG;QACd,IAAI,CAAC,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC/D,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QACpC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC;QAC9B,MAAM,gBAAgB,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAEpD,IAAI,CAAC,GAAG,CAAC,0BAA0B,UAAU,sBAAsB,CAAC,CAAC;QACrE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,gBAAgB,EAAE,CAAC,CAAC;QAE5D,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACjC,MAAM,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;QACpE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAEpB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,QAAQ,IAAI,CAAC,CAAC;QAEhE,IAAI,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;QACxC,IAAI,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,GAAG,CAAC,mBAAmB,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC;QAExD,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,OAAe,EAAE,QAAiB,EAAE,gBAAyB;QAC1F,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAEjD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAE9C,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACvB,MAAM,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;YACpE,CAAC;iBAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,CAAC,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,QAAgB,EAAE,QAAiB,EAAE,gBAAyB;QAC7F,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,QAAQ,EAAE,CAAC,CAAC;QAElD,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC7D,MAAM,OAAO,GAAG,MAAM,0BAA0B,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAE3E,iEAAiE;YACjE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,QAAQ,iBAAiB,OAAO,EAAE,IAAI,EAAE,WAAW,IAAI,WAAW,GAAG,CAAC,CAAC;gBACpH,OAAO;YACT,CAAC;YAED,6DAA6D;YAC7D,IAAI,gBAAgB,IAAI,0BAA0B,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC3E,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,uCAAuC,QAAQ,EAAE,CAAC,CAAC;gBACpE,OAAO;YACT,CAAC;YAED,MAAM,gBAAgB,GAAG,0BAA0B,CAAC,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;YACxG,MAAM,UAAU,GAAG,0BAA0B,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;YAEjF,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;gBAC3B,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;oBAClD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,QAAQ,EAAE,CAAC,CAAC;gBAC5C,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;gBAChD,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC;YAC9D,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC","sourcesContent":["/* eslint-disable no-await-in-loop */\n/* eslint-disable @typescript-eslint/return-await */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-unsafe-call */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-unsafe-return */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { SfCommand, Flags } from '@salesforce/sf-plugins-core';\nimport { Messages, Logger } from '@salesforce/core';\nimport * as xml2js from 'xml2js';\n\nexport type RflibLoggingFlowInstrumentResult = {\n processedFiles: number;\n modifiedFiles: number;\n};\n\nMessages.importMessagesDirectoryFromMetaUrl(import.meta.url);\nconst messages = Messages.loadMessages('rflib-plugin', 'rflib.logging.flow.instrument');\n\n// Type definition removed to fix compiler error\n// This was used to document valid Flow variable types: 'String' | 'Number' | 'Boolean' | 'SObject' | 'SObjectCollection'\n\nexport class FlowInstrumentationService {\n private static readonly parser = new xml2js.Parser({\n explicitArray: false,\n preserveChildrenOrder: true,\n xmlns: false\n });\n\n private static readonly builder = new xml2js.Builder({\n xmldec: { version: '1.0', encoding: 'UTF-8' },\n cdata: true\n });\n\n public static async parseFlowContent(content: string): Promise<any> {\n try {\n return await this.parser.parseStringPromise(content);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Flow parsing failed: ${error.message}`);\n }\n throw new Error('Flow parsing failed with unknown error');\n }\n }\n\n public static buildFlowContent(flowObj: any): string {\n try {\n return this.builder.buildObject(flowObj);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Flow building failed: ${error.message}`);\n }\n throw new Error('Flow building failed with unknown error');\n }\n }\n\n // Helper to check if flow already contains RFLIB logger\n public static hasRFLIBLogger(flowObj: any): boolean {\n if (!flowObj?.Flow?.actionCalls) {\n return false;\n }\n\n const actionCalls = Array.isArray(flowObj.Flow.actionCalls)\n ? flowObj.Flow.actionCalls\n : [flowObj.Flow.actionCalls];\n\n return actionCalls.some(\n (action: any) => \n action.actionName === 'rflib:Logger' || \n action.actionName === 'rflib_LoggerFlowAction' ||\n action.actionName === 'rflib_ApplicationEventLoggerAction' ||\n (action.name && typeof action.name === 'string' && action.name.startsWith('RFLIB_Flow_Logger'))\n );\n }\n\n // Helper to check if flow is of type that we want to instrument\n public static isFlowType(flowObj: any): boolean {\n return flowObj?.Flow?.processType === 'Flow';\n }\n\n // Main instrumentation function\n public static instrumentFlow(flowObj: any, flowName: string, skipInstrumented = false): any {\n // Deep clone the object to avoid modifying the original\n const instrumentedFlow = JSON.parse(JSON.stringify(flowObj));\n\n // Skip if already instrumented and skipInstrumented flag is set\n if (skipInstrumented && this.hasRFLIBLogger(instrumentedFlow)) {\n return instrumentedFlow;\n }\n\n // Make sure Flow exists in the object\n if (!instrumentedFlow.Flow) {\n return instrumentedFlow;\n }\n\n // Create logging action element\n let loggingAction = this.createLoggingAction(flowName);\n\n // Add variables to the logging message if available\n loggingAction = this.enhanceLoggingWithVariables(loggingAction, instrumentedFlow);\n\n // Add logging action to actionCalls\n if (!instrumentedFlow.Flow.actionCalls) {\n instrumentedFlow.Flow.actionCalls = loggingAction;\n } else if (Array.isArray(instrumentedFlow.Flow.actionCalls)) {\n instrumentedFlow.Flow.actionCalls.push(loggingAction);\n } else {\n // If only one action exists, convert to array\n instrumentedFlow.Flow.actionCalls = [instrumentedFlow.Flow.actionCalls, loggingAction];\n }\n\n // Find startElementReference and connect logger to it\n if (instrumentedFlow.Flow.startElementReference) {\n // Save the original start reference\n const startNodeReference = instrumentedFlow.Flow.startElementReference;\n \n // Create connector between logger and original start element\n loggingAction.connector = {\n targetReference: startNodeReference\n };\n \n // Update flow startElementReference to point to our logger\n instrumentedFlow.Flow.startElementReference = loggingAction.name;\n } else {\n // If no start element, try to find another entry point\n // Common patterns: decisions, screens, or first element in the process\n if (Array.isArray(instrumentedFlow.Flow.decisions) && instrumentedFlow.Flow.decisions.length > 0) {\n // Find the first decision and connect to it\n const firstDecision = instrumentedFlow.Flow.decisions[0];\n loggingAction.connector = {\n targetReference: firstDecision.name\n };\n } else if (Array.isArray(instrumentedFlow.Flow.screens) && instrumentedFlow.Flow.screens.length > 0) {\n // Find the first screen and connect to it\n const firstScreen = instrumentedFlow.Flow.screens[0];\n loggingAction.connector = {\n targetReference: firstScreen.name\n };\n }\n\n // Create a startElementReference pointing to our logger if none exists\n instrumentedFlow.Flow.startElementReference = loggingAction.name;\n }\n\n // Add interviewLabel if not present\n if (!instrumentedFlow.Flow.interviewLabel) {\n instrumentedFlow.Flow.interviewLabel = `${flowName} {!$Flow.CurrentDateTime}`;\n }\n\n // Ensure processType is set to 'Flow'\n instrumentedFlow.Flow.processType = 'Flow';\n\n // Add variables if not present (needed for variable references)\n if (!instrumentedFlow.Flow.variables) {\n instrumentedFlow.Flow.variables = [];\n } else if (!Array.isArray(instrumentedFlow.Flow.variables)) {\n instrumentedFlow.Flow.variables = [instrumentedFlow.Flow.variables];\n }\n\n // Instrument decisions with logging for each outcome\n if (instrumentedFlow.Flow.decisions) {\n this.instrumentDecisions(instrumentedFlow, flowName);\n }\n\n return instrumentedFlow;\n }\n \n // Helper to instrument decision paths with logging\n private static instrumentDecisions(flowObj: any, flowName: string): void {\n if (!flowObj.Flow.decisions) {\n return;\n }\n\n // Convert to array if there's only one decision\n const decisions = Array.isArray(flowObj.Flow.decisions) \n ? flowObj.Flow.decisions \n : [flowObj.Flow.decisions];\n \n // Process each decision\n decisions.forEach((decision: any) => {\n // Skip if decision doesn't have a name\n if (!decision.name) {\n return;\n }\n\n const decisionName = decision.name;\n const decisionLabel = decision.label || decisionName;\n \n // Process default connector if it exists\n if (decision.defaultConnector?.targetReference) {\n const defaultTarget = decision.defaultConnector.targetReference;\n const defaultConnectorLabel = decision.defaultConnectorLabel || 'Default Outcome';\n \n // Create a logger for the default path\n const defaultLogger = this.createDecisionPathLogger(\n flowName, \n String(decisionName), \n String(decisionLabel),\n 'default',\n String(defaultConnectorLabel)\n );\n \n // Connect logger to the original target\n defaultLogger.connector = {\n targetReference: defaultTarget\n };\n \n // Add logger to actionCalls first, before updating the decision connector\n this.addActionCallToFlow(flowObj, defaultLogger);\n \n // Update the decision's default connector to point to our logger\n // We're inside a forEach callback, so we have to modify the original object\n /* eslint-disable no-param-reassign */\n decision.defaultConnector.targetReference = defaultLogger.name;\n /* eslint-enable no-param-reassign */\n }\n \n // Process each rule if they exist\n if (decision.rules) {\n const rules = Array.isArray(decision.rules) ? decision.rules : [decision.rules];\n \n rules.forEach((rule: any) => {\n // Skip if rule doesn't have a connector or name\n if (!rule.connector?.targetReference || !rule.name) {\n return;\n }\n \n const ruleTarget = rule.connector.targetReference;\n const ruleName = rule.name;\n const ruleLabel = rule.label || ruleName;\n \n // Create a logger for this rule outcome\n const ruleLogger = this.createDecisionPathLogger(\n flowName, \n String(decisionName), \n String(decisionLabel),\n String(ruleName),\n String(ruleLabel)\n );\n \n // Connect logger to the original target\n ruleLogger.connector = {\n targetReference: ruleTarget\n };\n \n // Add logger to actionCalls first, before updating the rule connector\n this.addActionCallToFlow(flowObj, ruleLogger);\n \n // Update the rule's connector to point to our logger\n // We're inside a forEach callback, so we have to modify the original object\n /* eslint-disable no-param-reassign */\n rule.connector.targetReference = ruleLogger.name;\n /* eslint-enable no-param-reassign */\n });\n }\n });\n }\n \n // Helper to add action calls to the flow object\n // Note: This method does modify the parameter directly - we accepted the eslint warning\n // since we need to modify the flow object within callback functions where returning a new value isn't possible\n private static addActionCallToFlow(flowObj: any, actionCall: any): void {\n /* eslint-disable no-param-reassign */\n if (!flowObj.Flow.actionCalls) {\n flowObj.Flow.actionCalls = actionCall;\n } else if (Array.isArray(flowObj.Flow.actionCalls)) {\n flowObj.Flow.actionCalls.push(actionCall);\n } else {\n // If only one action exists, convert to array\n flowObj.Flow.actionCalls = [flowObj.Flow.actionCalls, actionCall];\n }\n /* eslint-enable no-param-reassign */\n }\n \n // Helper to create a logging action for decision paths\n private static createDecisionPathLogger(\n flowName: string, \n decisionName: string, \n decisionLabel: string,\n outcomeName: string,\n outcomeLabel: string\n ): any {\n const loggerId = this.generateUniqueId();\n \n // Ensure we're working with strings\n const decisionNameStr = String(decisionName);\n const outcomeNameStr = String(outcomeName);\n const decisionLabelStr = String(decisionLabel);\n const outcomeLabelStr = String(outcomeLabel);\n \n // Calculate maximum lengths to stay under 80 characters\n const prefixLength = 'RFLIB_Flow_Logger_Decision_'.length;\n const separatorsLength = 2; // Two underscores\n const maxTotalNameLength = 80 - prefixLength - loggerId.length - separatorsLength;\n \n // Allocate half of available space to each name (decision and outcome)\n const maxIndividualLength = Math.floor(maxTotalNameLength / 2);\n \n // Sanitize names to fit Salesforce naming rules\n const sanitizedDecisionName = this.sanitizeForName(decisionNameStr, maxIndividualLength);\n const sanitizedOutcomeName = this.sanitizeForName(outcomeNameStr, maxIndividualLength);\n \n // Create a name that's guaranteed to be under 80 chars and follow Salesforce rules\n const name = `RFLIB_Flow_Logger_Decision_${sanitizedDecisionName}_${sanitizedOutcomeName}_${loggerId}`;\n \n // Create and truncate the label to ensure it's under 80 chars\n const label = this.truncateLabel(`Log Decision: ${decisionLabelStr} - ${outcomeLabelStr}`);\n \n // Fallback if still too long\n if (name.length > 80) {\n return {\n actionName: 'rflib_LoggerFlowAction',\n actionType: 'apex',\n name: `RFLIBLogDec${loggerId}`,\n label,\n locationX: 176,\n locationY: 50,\n inputParameters: [\n {\n name: 'context',\n value: {\n stringValue: flowName,\n },\n },\n {\n name: 'logLevel',\n value: {\n stringValue: 'INFO',\n },\n },\n {\n name: 'message',\n value: {\n stringValue: `Decision '${decisionLabelStr}' outcome: ${outcomeLabelStr}`,\n },\n },\n ],\n };\n }\n \n return {\n actionName: 'rflib_LoggerFlowAction',\n actionType: 'apex',\n name,\n label,\n locationX: 176,\n locationY: 50,\n inputParameters: [\n {\n name: 'context',\n value: {\n stringValue: flowName,\n },\n },\n {\n name: 'logLevel',\n value: {\n stringValue: 'INFO',\n },\n },\n {\n name: 'message',\n value: {\n stringValue: `Decision '${decisionLabelStr}' outcome: ${outcomeLabelStr}`,\n },\n },\n ],\n };\n }\n\n // Helper to generate unique IDs for new flow elements (compact for 80-char limit)\n // that follow Salesforce Flow Action naming rules\n private static generateUniqueId(): string {\n // Use timestamp in base36 + 4 random chars to keep it short but unique\n // Ensure we don't start with a number or have consecutive/trailing underscores\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 6);\n \n // Combine parts without underscore to avoid potential consecutive underscores\n return `ID${timestamp}${random}`;\n }\n \n // Helper to sanitize and truncate text to fit within the 80-char name limit\n // and to follow Salesforce Flow Action naming rules:\n // - Only alphanumeric characters and underscores\n // - Must begin with a letter\n // - No spaces\n // - No underscore at the end\n // - No consecutive underscores\n private static sanitizeForName(text: string, maxLength: number): string {\n if (!text) {\n return 'X'; // Default to 'X' for empty inputs to ensure we start with a letter\n }\n \n // First, replace any non-alphanumeric characters with underscores\n let sanitized = text.replace(/[^a-zA-Z0-9_]/g, '_');\n \n // Ensure it starts with a letter\n if (!/^[a-zA-Z]/.test(sanitized)) {\n sanitized = 'X' + sanitized;\n }\n \n // Replace consecutive underscores with a single underscore\n sanitized = sanitized.replace(/__+/g, '_');\n \n // Remove trailing underscore if present\n sanitized = sanitized.replace(/_+$/, '');\n \n // If empty after sanitization, return a default value\n if (!sanitized) {\n return 'X';\n }\n \n // Truncate if longer than maxLength\n if (sanitized.length > maxLength) {\n // Truncate and ensure it doesn't end with an underscore\n sanitized = sanitized.substring(0, maxLength).replace(/_+$/, '');\n \n // If we removed trailing underscores and now it's empty or too short, add a fallback\n if (sanitized.length < 1) {\n sanitized = 'X';\n }\n }\n \n return sanitized;\n }\n\n // Helper to truncate label text to fit within the 80-char limit\n private static truncateLabel(label: string, maxLength: number = 80): string {\n if (!label || label.length <= maxLength) {\n return label;\n }\n \n // If text is too long, truncate it and add ellipsis\n return label.substring(0, maxLength - 3) + '...';\n }\n\n // Helper to create a logging action element\n private static createLoggingAction(flowName: string): any {\n const loggerId = this.generateUniqueId();\n \n // Ensure name is under 80 characters and follows Salesforce naming rules\n const prefixLength = 'RFLIB_Flow_Logger_'.length;\n const maxFlowNameLength = 80 - prefixLength - loggerId.length - 1; // -1 for underscore\n const sanitizedFlowName = this.sanitizeForName(flowName, maxFlowNameLength);\n \n // Create a name that's guaranteed to be under 80 chars and follow Salesforce rules\n const name = `RFLIB_Flow_Logger_${sanitizedFlowName}_${loggerId}`;\n \n // Create and truncate the label to ensure it's under 80 chars\n const label = this.truncateLabel(`Log Flow Invocation: ${flowName}`);\n \n // Verify name length\n if (name.length > 80) {\n // If still too long, use a simpler naming scheme (fallback)\n return {\n actionName: 'rflib_LoggerFlowAction',\n actionType: 'apex',\n name: `RFLIBLogger${loggerId}`,\n label,\n locationX: 176,\n locationY: 50,\n inputParameters: [\n {\n name: 'context',\n value: {\n stringValue: flowName,\n },\n },\n {\n name: 'logLevel',\n value: {\n stringValue: 'INFO',\n },\n },\n {\n name: 'message',\n value: {\n stringValue: `Flow ${flowName} started`,\n },\n },\n ],\n };\n }\n \n return {\n actionName: 'rflib_LoggerFlowAction',\n actionType: 'apex',\n name,\n label,\n locationX: 176,\n locationY: 50,\n inputParameters: [\n {\n name: 'context',\n value: {\n stringValue: flowName,\n },\n },\n {\n name: 'logLevel',\n value: {\n stringValue: 'INFO',\n },\n },\n {\n name: 'message',\n value: {\n stringValue: `Flow ${flowName} started`,\n },\n },\n ],\n };\n }\n\n // Helper to add variable references to the logging message when available\n private static enhanceLoggingWithVariables(loggingAction: any, flowObj: any): any {\n // Find input variables or parameters that might be useful to log\n const variables = flowObj.Flow.variables || [];\n const inputVariables = Array.isArray(variables)\n ? variables.filter((v: any) => v.isInput === 'true' || v.isCollection === 'true')\n : (variables.isInput === 'true' || variables.isCollection === 'true' ? [variables] : []);\n\n if (inputVariables.length > 0) {\n // Find the message parameter - case insensitive search\n const messageParamIndex = loggingAction.inputParameters.findIndex(\n (p: any) => p.name?.toLowerCase() === 'message'\n );\n\n if (messageParamIndex >= 0) {\n // Enhance the message with variable information\n const varRefs = inputVariables\n .map((v: any) => `${v.name}: {!${v.name}}`)\n .join(', ');\n\n const baseMessage = loggingAction.inputParameters[messageParamIndex];\n const originalMessage = baseMessage.value.stringValue;\n baseMessage.value.stringValue = `${originalMessage} with ${varRefs}`;\n }\n }\n\n return loggingAction;\n }\n}\n\nexport default class RflibLoggingFlowInstrument extends SfCommand<RflibLoggingFlowInstrumentResult> {\n public static readonly summary = messages.getMessage('summary');\n public static readonly description = messages.getMessage('description');\n public static readonly examples = messages.getMessages('examples');\n\n public static readonly flags = {\n sourcepath: Flags.string({\n summary: messages.getMessage('flags.sourcepath.summary'),\n description: messages.getMessage('flags.sourcepath.description'),\n char: 's',\n required: true,\n }),\n dryrun: Flags.boolean({\n summary: messages.getMessage('flags.dryrun.summary'),\n description: messages.getMessage('flags.dryrun.description'),\n char: 'd',\n default: false,\n }),\n 'skip-instrumented': Flags.boolean({\n summary: messages.getMessage('flags.skip-instrumented.summary') || 'Skip flows that already have RFLIB logging',\n description: messages.getMessage('flags.skip-instrumented.description') || 'Do not instrument flows where RFLIB logging is already present',\n default: false,\n }),\n };\n\n private logger!: Logger;\n private readonly stats: RflibLoggingFlowInstrumentResult = {\n processedFiles: 0,\n modifiedFiles: 0,\n };\n\n public async run(): Promise<RflibLoggingFlowInstrumentResult> {\n this.logger = await Logger.child(this.ctor.name);\n const startTime = Date.now();\n\n const { flags } = await this.parse(RflibLoggingFlowInstrument);\n const sourcePath = flags.sourcepath;\n const isDryRun = flags.dryrun;\n const skipInstrumented = flags['skip-instrumented'];\n\n this.log(`Scanning Flow files in ${sourcePath} and sub directories`);\n this.logger.debug(`Dry run mode: ${isDryRun}`);\n this.logger.debug(`Skip instrumented: ${skipInstrumented}`);\n\n this.spinner.start('Running...');\n await this.processDirectory(sourcePath, isDryRun, skipInstrumented);\n this.spinner.stop();\n\n const duration = Date.now() - startTime;\n this.logger.debug(`Completed instrumentation in ${duration}ms`);\n\n this.log('\\nInstrumentation complete.');\n this.log(`Processed files: ${this.stats.processedFiles}`);\n this.log(`Modified files: ${this.stats.modifiedFiles}`);\n\n return { ...this.stats };\n }\n\n private async processDirectory(dirPath: string, isDryRun: boolean, skipInstrumented: boolean): Promise<void> {\n this.logger.debug(`Processing directory: ${dirPath}`);\n const files = await fs.promises.readdir(dirPath);\n\n for (const file of files) {\n const filePath = path.join(dirPath, file);\n const stat = await fs.promises.stat(filePath);\n\n if (stat.isDirectory()) {\n await this.processDirectory(filePath, isDryRun, skipInstrumented);\n } else if (file.endsWith('.flow-meta.xml')) {\n await this.instrumentFlowFile(filePath, isDryRun, skipInstrumented);\n }\n }\n }\n\n private async instrumentFlowFile(filePath: string, isDryRun: boolean, skipInstrumented: boolean): Promise<void> {\n const flowName = path.basename(filePath, '.flow-meta.xml');\n this.logger.debug(`Processing flow: ${flowName}`);\n\n try {\n this.stats.processedFiles++;\n const content = await fs.promises.readFile(filePath, 'utf8');\n const flowObj = await FlowInstrumentationService.parseFlowContent(content);\n\n // Only instrument flows with processType=\"Flow\", skip all others\n if (!FlowInstrumentationService.isFlowType(flowObj)) {\n this.logger.debug(`Skipping non-Flow type: ${flowName} (processType=${flowObj?.Flow?.processType || 'undefined'})`);\n return;\n }\n\n // Check if flow already has RFLIB logging and skip if needed\n if (skipInstrumented && FlowInstrumentationService.hasRFLIBLogger(flowObj)) {\n this.logger.info(`Skipping already instrumented flow: ${flowName}`);\n return;\n }\n\n const instrumentedFlow = FlowInstrumentationService.instrumentFlow(flowObj, flowName, skipInstrumented);\n const newContent = FlowInstrumentationService.buildFlowContent(instrumentedFlow);\n\n if (content !== newContent) {\n this.stats.modifiedFiles++;\n if (!isDryRun) {\n await fs.promises.writeFile(filePath, newContent);\n this.logger.info(`Modified: ${filePath}`);\n } else {\n this.logger.info(`Would modify: ${filePath}`);\n }\n }\n } catch (error) {\n this.logger.error(`Error processing flow ${flowName}`, error);\n throw error;\n }\n }\n}"]}
|
|
1
|
+
{"version":3,"file":"instrument.js","sourceRoot":"","sources":["../../../../../src/commands/rflib/logging/flow/instrument.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable no-await-in-loop, sf-plugin/only-extend-SfCommand */\n/* eslint-disable @typescript-eslint/return-await */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-unsafe-call */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-unsafe-return */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { Messages, Logger } from '@salesforce/core';\nimport { SfCommand, Flags } from '@salesforce/sf-plugins-core';\nimport * as xml2js from 'xml2js';\n\nexport type RflibLoggingFlowInstrumentResult = {\n processedFiles: number;\n modifiedFiles: number;\n};\n\nMessages.importMessagesDirectoryFromMetaUrl(import.meta.url);\nconst messages = Messages.loadMessages('rflib-plugin', 'rflib.logging.flow.instrument');\n\nexport class FlowInstrumentationService {\n private static readonly parser = new xml2js.Parser({\n explicitArray: false,\n preserveChildrenOrder: true,\n xmlns: false\n });\n\n private static readonly builder = new xml2js.Builder({\n xmldec: { version: '1.0', encoding: 'UTF-8' },\n cdata: true,\n renderOpts: {\n pretty: true,\n indent: ' ',\n newline: '\\n'\n }\n });\n\n public static async parseFlowContent(content: string): Promise<any> {\n try {\n return await this.parser.parseStringPromise(content);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Flow parsing failed: ${error.message}`);\n }\n throw new Error('Flow parsing failed with unknown error');\n }\n }\n\n public static buildFlowContent(flowObj: any): string {\n try {\n if (!flowObj?.Flow) {\n throw new Error('Invalid flow object structure');\n }\n\n // Create a new Flow object with ordered properties\n const orderedFlow = {\n '$': { 'xmlns': 'http://soap.sforce.com/2006/04/metadata' }\n } as Record<string, unknown>;\n\n // Add actionCalls first if it exists\n if (flowObj.Flow.actionCalls) {\n orderedFlow.actionCalls = flowObj.Flow.actionCalls;\n }\n\n // Add all other properties in their original order\n Object.entries(flowObj.Flow as Record<string, unknown>).forEach(([key, value]) => {\n if (key !== 'actionCalls' && key !== '$') {\n orderedFlow[key] = value;\n }\n });\n\n // Use the builder with just the Flow object, not wrapped in another object\n return this.builder.buildObject({ Flow: orderedFlow });\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Flow building failed: ${error.message}`);\n }\n throw new Error('Flow building failed with unknown error');\n }\n }\n\n // Helper to check if flow already contains RFLIB logger\n public static hasRFLIBLogger(flowObj: any): boolean {\n if (!flowObj?.Flow?.actionCalls) {\n return false;\n }\n\n const actionCalls = Array.isArray(flowObj.Flow.actionCalls)\n ? flowObj.Flow.actionCalls\n : [flowObj.Flow.actionCalls];\n\n return actionCalls.some(\n (action: any) =>\n action.actionName === 'rflib:Logger' ||\n action.actionName === 'rflib_LoggerFlowAction' ||\n action.actionName === 'rflib_ApplicationEventLoggerAction' ||\n (action.name && typeof action.name === 'string' && action.name.startsWith('RFLIB_Flow_Logger'))\n );\n }\n\n // Helper to check if flow has a supported process type for instrumentation\n public static isSupportedProcessType(flowObj: any): boolean {\n const processType = flowObj?.Flow?.processType;\n const triggerType = flowObj?.Flow?.start?.triggerType;\n\n return processType === 'Flow' ||\n (processType === 'AutoLaunchedFlow' && triggerType === 'RecordAfterSave');\n }\n\n // Main instrumentation function\n public static instrumentFlow(flowObj: any, flowName: string, skipInstrumented = false): any {\n // Deep clone the object to avoid modifying the original\n const instrumentedFlow = JSON.parse(JSON.stringify(flowObj));\n\n // Skip if already instrumented and skipInstrumented flag is set\n if (skipInstrumented && this.hasRFLIBLogger(instrumentedFlow)) {\n return instrumentedFlow;\n }\n\n // Make sure Flow exists in the object\n if (!instrumentedFlow.Flow) {\n return instrumentedFlow;\n }\n\n // Create logging action element\n let loggingAction = this.createLoggingAction(flowName);\n\n // Add variables to the logging message if available\n loggingAction = this.enhanceLoggingWithVariables(loggingAction, instrumentedFlow);\n\n // Add logging action to actionCalls\n if (!instrumentedFlow.Flow.actionCalls) {\n instrumentedFlow.Flow.actionCalls = loggingAction;\n } else if (Array.isArray(instrumentedFlow.Flow.actionCalls)) {\n instrumentedFlow.Flow.actionCalls.unshift(loggingAction);\n } else {\n instrumentedFlow.Flow.actionCalls = [loggingAction, instrumentedFlow.Flow.actionCalls];\n }\n\n // Find startElementReference or start element and connect logger to it\n if (instrumentedFlow.Flow.startElementReference) {\n // Save the original start reference\n const startNodeReference = instrumentedFlow.Flow.startElementReference;\n\n // Create connector between logger and original start element\n loggingAction.connector = {\n targetReference: startNodeReference\n };\n\n // Update flow startElementReference to point to our logger\n instrumentedFlow.Flow.startElementReference = loggingAction.name;\n } else if (instrumentedFlow.Flow.start?.connector?.targetReference) {\n // Handle flow with start element: create connector and update start reference\n const startElement = instrumentedFlow.Flow.start;\n const originalTarget = startElement.connector.targetReference;\n\n // Create connector between logger and the original target\n loggingAction.connector = {\n targetReference: originalTarget\n };\n\n // Update the start element connector to point to our logger\n startElement.connector.targetReference = loggingAction.name;\n // Also set the startElementReference for consistency\n instrumentedFlow.Flow.startElementReference = loggingAction.name;\n }\n\n // Set the CanvasMode to AUTO_LAYOUT_CANVAS\n this.setCanvasMode(instrumentedFlow);\n\n // Instrument decisions with logging for each outcome\n if (instrumentedFlow.Flow.decisions) {\n this.instrumentDecisions(instrumentedFlow, flowName);\n }\n\n // Reorder Flow properties\n const originalFlow = instrumentedFlow.Flow as Record<string, unknown>;\n const orderedFlow: Record<string, unknown> = {\n $: originalFlow.$,\n actionCalls: originalFlow.actionCalls\n };\n\n Object.keys(originalFlow).forEach(key => {\n if (key !== 'actionCalls' && key !== '$') {\n orderedFlow[key] = originalFlow[key];\n }\n });\n\n instrumentedFlow.Flow = orderedFlow;\n return instrumentedFlow;\n }\n\n // Helper to instrument decision paths with logging\n private static instrumentDecisions(flowObj: any, flowName: string): void {\n if (!flowObj.Flow.decisions) {\n return;\n }\n\n // Convert to array if there's only one decision\n const decisions = Array.isArray(flowObj.Flow.decisions)\n ? flowObj.Flow.decisions\n : [flowObj.Flow.decisions];\n\n // Process each decision\n decisions.forEach((decision: any) => {\n // Support decision name as 'name' or legacy 'n'\n const decisionNameRaw = decision.name ?? decision.n;\n if (!decisionNameRaw) {\n return;\n }\n const decisionName = decisionNameRaw;\n const decisionLabel = decision.label || decisionName;\n\n // Process default connector if it exists\n if (decision.defaultConnector?.targetReference) {\n const defaultTarget = decision.defaultConnector.targetReference;\n const defaultConnectorLabel = decision.defaultConnectorLabel || 'Default Outcome';\n\n // Create a logger for the default path\n const defaultLogger = this.createDecisionPathLogger(\n flowName,\n String(decisionName),\n String(decisionLabel),\n 'default',\n String(defaultConnectorLabel)\n );\n\n // Connect logger to the original target\n defaultLogger.connector = {\n targetReference: defaultTarget\n };\n\n // Add logger to actionCalls first, before updating the decision connector\n this.addActionCallToFlow(flowObj, defaultLogger);\n\n // Update the decision's default connector to point to our logger\n // We're inside a forEach callback, so we have to modify the original object\n /* eslint-disable no-param-reassign */\n decision.defaultConnector.targetReference = defaultLogger.name;\n /* eslint-enable no-param-reassign */\n }\n\n // Process each rule if they exist\n if (decision.rules) {\n const rules = Array.isArray(decision.rules) ? decision.rules : [decision.rules];\n\n rules.forEach((rule: any) => {\n // Skip if rule doesn't have a connector or name (support 'name' or legacy 'n')\n const ruleNameRaw = rule.name ?? rule.n;\n if (!rule.connector?.targetReference || !ruleNameRaw) {\n return;\n }\n const ruleTarget = rule.connector.targetReference;\n const ruleName = ruleNameRaw;\n const ruleLabel = rule.label || ruleName;\n\n // Create a logger for this rule outcome\n const ruleLogger = this.createDecisionPathLogger(\n flowName,\n String(decisionName),\n String(decisionLabel),\n String(ruleName),\n String(ruleLabel)\n );\n\n // Connect logger to the original target\n ruleLogger.connector = {\n targetReference: ruleTarget\n };\n\n // Add logger to actionCalls first, before updating the rule connector\n this.addActionCallToFlow(flowObj, ruleLogger);\n\n // Update the rule's connector to point to our logger\n // We're inside a forEach callback, so we have to modify the original object\n /* eslint-disable no-param-reassign */\n rule.connector.targetReference = ruleLogger.name;\n /* eslint-enable no-param-reassign */\n });\n }\n });\n }\n\n // Helper to add action calls to the flow object\n // Note: This method does modify the parameter directly - we accepted the eslint warning\n // since we need to modify the flow object within callback functions where returning a new value isn't possible\n private static addActionCallToFlow(flowObj: any, actionCall: any): void {\n /* eslint-disable no-param-reassign */\n if (!flowObj.Flow.actionCalls) {\n flowObj.Flow.actionCalls = actionCall;\n } else if (Array.isArray(flowObj.Flow.actionCalls)) {\n // Add new action at the beginning of the array\n flowObj.Flow.actionCalls.unshift(actionCall);\n } else {\n // If only one action exists, convert to array with new action first\n flowObj.Flow.actionCalls = [actionCall, flowObj.Flow.actionCalls];\n }\n /* eslint-enable no-param-reassign */\n }\n\n // Helper to create a logging action for decision paths\n private static createDecisionPathLogger(\n flowName: string,\n decisionName: string,\n decisionLabel: string,\n outcomeName: string,\n outcomeLabel: string\n ): any {\n const loggerId = this.generateUniqueId();\n\n // Ensure we're working with strings\n const decisionNameStr = String(decisionName);\n const outcomeNameStr = String(outcomeName);\n const decisionLabelStr = String(decisionLabel);\n const outcomeLabelStr = String(outcomeLabel);\n\n // Calculate maximum lengths to stay under 80 characters\n const prefixLength = 'RFLIB_Flow_Logger_Decision_'.length;\n const separatorsLength = 2; // Two underscores\n const maxTotalNameLength = 80 - prefixLength - loggerId.length - separatorsLength;\n\n // Allocate half of available space to each name (decision and outcome)\n const maxIndividualLength = Math.floor(maxTotalNameLength / 2);\n\n // Sanitize names to fit Salesforce naming rules\n const sanitizedDecisionName = this.sanitizeForName(decisionNameStr, maxIndividualLength);\n const sanitizedOutcomeName = this.sanitizeForName(outcomeNameStr, maxIndividualLength);\n\n // Create a name that's guaranteed to be under 80 chars and follow Salesforce rules\n const name = `RFLIB_Flow_Logger_Decision_${sanitizedDecisionName}_${sanitizedOutcomeName}_${loggerId}`;\n\n // Create and truncate the label to ensure it's under 80 chars\n const label = this.truncateLabel(`Log Decision: ${decisionLabelStr} - ${outcomeLabelStr}`);\n\n // Fallback if still too long\n if (name.length > 80) {\n return {\n actionName: 'rflib_LoggerFlowAction',\n actionType: 'apex',\n name: `RFLIBLogDec${loggerId}`,\n label,\n locationX: 176,\n locationY: 50,\n inputParameters: [\n {\n name: 'context',\n value: {\n stringValue: flowName,\n },\n },\n {\n name: 'logLevel',\n value: {\n stringValue: 'INFO',\n },\n },\n {\n name: 'message',\n value: {\n stringValue: `Decision '${decisionLabelStr}' outcome: ${outcomeLabelStr}`,\n },\n },\n ],\n };\n }\n\n return {\n actionName: 'rflib_LoggerFlowAction',\n actionType: 'apex',\n name,\n label,\n locationX: 176,\n locationY: 50,\n inputParameters: [\n {\n name: 'context',\n value: {\n stringValue: flowName,\n },\n },\n {\n name: 'logLevel',\n value: {\n stringValue: 'INFO',\n },\n },\n {\n name: 'message',\n value: {\n stringValue: `Decision '${decisionLabelStr}' outcome: ${outcomeLabelStr}`,\n },\n },\n ],\n };\n }\n\n // Helper to generate unique IDs for new flow elements (compact for 80-char limit)\n // that follow Salesforce Flow Action naming rules\n private static generateUniqueId(): string {\n // Use timestamp in base36 + 4 random chars to keep it short but unique\n // Ensure we don't start with a number or have consecutive/trailing underscores\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 6);\n\n // Combine parts without underscore to avoid potential consecutive underscores\n return `ID${timestamp}${random}`;\n }\n\n // Helper to sanitize and truncate text to fit within the 80-char name limit\n // and to follow Salesforce Flow Action naming rules:\n // - Only alphanumeric characters and underscores\n // - Must begin with a letter\n // - No spaces\n // - No underscore at the end\n // - No consecutive underscores\n private static sanitizeForName(text: string, maxLength: number): string {\n if (!text) {\n return 'X'; // Default to 'X' for empty inputs to ensure we start with a letter\n }\n\n // First, replace any non-alphanumeric characters with underscores\n let sanitized = text.replace(/[^a-zA-Z0-9_]/g, '_');\n\n // Ensure it starts with a letter\n if (!/^[a-zA-Z]/.test(sanitized)) {\n sanitized = 'X' + sanitized;\n }\n\n // Replace consecutive underscores with a single underscore\n sanitized = sanitized.replace(/__+/g, '_');\n\n // Remove trailing underscore if present\n sanitized = sanitized.replace(/_+$/, '');\n\n // If empty after sanitization, return a default value\n if (!sanitized) {\n return 'X';\n }\n\n // Truncate if longer than maxLength\n if (sanitized.length > maxLength) {\n // Truncate and ensure it doesn't end with an underscore\n sanitized = sanitized.substring(0, maxLength).replace(/_+$/, '');\n\n // If we removed trailing underscores and now it's empty or too short, add a fallback\n if (sanitized.length < 1) {\n sanitized = 'X';\n }\n }\n\n return sanitized;\n }\n\n // Helper to truncate label text to fit within the 80-char limit\n private static truncateLabel(label: string, maxLength: number = 80): string {\n if (!label || label.length <= maxLength) {\n return label;\n }\n\n // If text is too long, truncate it and add ellipsis\n return label.substring(0, maxLength - 3) + '...';\n }\n\n // Helper to create a logging action element\n private static createLoggingAction(flowName: string): any {\n const loggerId = this.generateUniqueId();\n\n // Create a name for the flow invocation logger (omit flowName to avoid conflicts)\n const name = `RFLIB_Flow_Logger_${loggerId}`;\n\n // Create and truncate the label to ensure it's under 80 chars\n const label = this.truncateLabel(`Log Flow Invocation: ${flowName}`);\n\n // Verify name length\n if (name.length > 80) {\n // If still too long, use a simpler naming scheme (fallback)\n return {\n actionName: 'rflib_LoggerFlowAction',\n actionType: 'apex',\n name: `RFLIBLogger${loggerId}`,\n label,\n locationX: 176,\n locationY: 50,\n inputParameters: [\n {\n name: 'context',\n value: {\n stringValue: flowName,\n },\n },\n {\n name: 'logLevel',\n value: {\n stringValue: 'INFO',\n },\n },\n {\n name: 'message',\n value: {\n stringValue: `Flow ${flowName} started`,\n },\n },\n ],\n };\n }\n\n return {\n actionName: 'rflib_LoggerFlowAction',\n actionType: 'apex',\n name,\n label,\n locationX: 176,\n locationY: 50,\n inputParameters: [\n {\n name: 'context',\n value: {\n stringValue: flowName,\n },\n },\n {\n name: 'logLevel',\n value: {\n stringValue: 'INFO',\n },\n },\n {\n name: 'message',\n value: {\n stringValue: `Flow ${flowName} started`,\n },\n },\n ],\n };\n }\n\n // Helper to set CanvasMode to AUTO_LAYOUT_CANVAS for better flow layout\n private static setCanvasMode(flowObj: any): void {\n // No longer automatically setting canvas mode - preserve original mode\n if (!flowObj.Flow) {\n return;\n }\n\n // Preserve original processMetadataValues state\n const originalMeta = flowObj.Flow.processMetadataValues;\n const hadProcessMetadataValues = !!originalMeta;\n // Normalize to array\n const metadataValues = !originalMeta\n ? []\n : Array.isArray(originalMeta)\n ? originalMeta\n : [originalMeta];\n\n // Prepare CanvasMode entry\n const canvasModeEntry = {\n name: 'CanvasMode',\n value: {\n stringValue: 'AUTO_LAYOUT_CANVAS'\n }\n };\n // Check if CanvasMode metadata exists\n const canvasModeIndex = metadataValues.findIndex((meta: any) =>\n meta.name === 'CanvasMode'\n );\n\n if (canvasModeIndex === -1) {\n // Add AUTO_LAYOUT_CANVAS entry\n metadataValues.push(canvasModeEntry);\n // Duplicate entry for flows that had no metadata to ensure array output on single entry\n if (!hadProcessMetadataValues) {\n metadataValues.push({\n name: canvasModeEntry.name,\n value: { stringValue: canvasModeEntry.value.stringValue }\n });\n }\n } else {\n metadataValues[canvasModeIndex].value.stringValue = 'AUTO_LAYOUT_CANVAS';\n }\n\n // Assign back the potentially modified array\n /* eslint-disable no-param-reassign */\n flowObj.Flow.processMetadataValues = metadataValues;\n /* eslint-enable no-param-reassign */\n }\n\n // Helper to add variable references to the logging message when available\n private static enhanceLoggingWithVariables(loggingAction: any, flowObj: any): any {\n // Find input variables or parameters that might be useful to log\n const variables = flowObj.Flow.variables || [];\n const inputVariables = Array.isArray(variables)\n ? variables.filter((v: any) => v.isInput === 'true' || v.isCollection === 'true')\n : (variables.isInput === 'true' || variables.isCollection === 'true' ? [variables] : []);\n\n if (inputVariables.length > 0) {\n // Find the message parameter - case insensitive search\n const messageParamIndex = loggingAction.inputParameters.findIndex(\n (p: any) => p.name?.toLowerCase() === 'message'\n );\n\n if (messageParamIndex >= 0) {\n // Enhance the message with variable information\n const varRefs = inputVariables\n .map((v: any) => `${v.name}: {!${v.name}}`)\n .join(', ');\n\n const baseMessage = loggingAction.inputParameters[messageParamIndex];\n const originalMessage = baseMessage.value.stringValue;\n baseMessage.value.stringValue = `${originalMessage} with ${varRefs}`;\n }\n }\n\n return loggingAction;\n }\n}\n\nexport default class RflibLoggingFlowInstrument extends SfCommand<RflibLoggingFlowInstrumentResult> {\n public static readonly summary = messages.getMessage('summary');\n public static readonly description = messages.getMessage('description');\n public static readonly examples = messages.getMessages('examples');\n\n public static readonly flags = {\n sourcepath: Flags.string({\n summary: messages.getMessage('flags.sourcepath.summary'),\n description: messages.getMessage('flags.sourcepath.description'),\n char: 's',\n required: true,\n }),\n dryrun: Flags.boolean({\n summary: messages.getMessage('flags.dryrun.summary'),\n description: messages.getMessage('flags.dryrun.description'),\n char: 'd',\n default: false,\n }),\n 'skip-instrumented': Flags.boolean({\n summary: messages.getMessage('flags.skip-instrumented.summary') || 'Skip flows that already have RFLIB logging',\n description: messages.getMessage('flags.skip-instrumented.description') || 'Do not instrument flows where RFLIB logging is already present',\n default: false,\n }),\n };\n\n private logger!: Logger;\n private readonly stats: RflibLoggingFlowInstrumentResult = {\n processedFiles: 0,\n modifiedFiles: 0,\n };\n\n public async run(): Promise<RflibLoggingFlowInstrumentResult> {\n this.logger = await Logger.child(this.ctor.name);\n const startTime = Date.now();\n\n const { flags } = await this.parse(RflibLoggingFlowInstrument);\n const sourcePath = flags.sourcepath;\n const isDryRun = flags.dryrun;\n const skipInstrumented = flags['skip-instrumented'];\n\n this.log(`Scanning Flow files in ${sourcePath} and sub directories`);\n this.logger.debug(`Dry run mode: ${isDryRun}`);\n this.logger.debug(`Skip instrumented: ${skipInstrumented}`);\n\n this.spinner.start('Running...');\n await this.processDirectory(sourcePath, isDryRun, skipInstrumented);\n this.spinner.stop();\n\n const duration = Date.now() - startTime;\n this.logger.debug(`Completed instrumentation in ${duration}ms`);\n\n this.log('\\nInstrumentation complete.');\n this.log(`Processed files: ${this.stats.processedFiles}`);\n this.log(`Modified files: ${this.stats.modifiedFiles}`);\n\n return { ...this.stats };\n }\n\n private async processDirectory(dirPath: string, isDryRun: boolean, skipInstrumented: boolean): Promise<void> {\n this.logger.debug(`Processing directory: ${dirPath}`);\n const files = await fs.promises.readdir(dirPath);\n\n for (const file of files) {\n const filePath = path.join(dirPath, file);\n const stat = await fs.promises.stat(filePath);\n\n if (stat.isDirectory()) {\n await this.processDirectory(filePath, isDryRun, skipInstrumented);\n } else if (file.endsWith('.flow-meta.xml')) {\n await this.instrumentFlowFile(filePath, isDryRun, skipInstrumented);\n }\n }\n }\n\n private async instrumentFlowFile(filePath: string, isDryRun: boolean, skipInstrumented: boolean): Promise<void> {\n const flowName = path.basename(filePath, '.flow-meta.xml');\n this.logger.debug(`Processing flow: ${flowName}`);\n\n try {\n this.stats.processedFiles++;\n const content = await fs.promises.readFile(filePath, 'utf8');\n const flowObj = await FlowInstrumentationService.parseFlowContent(content);\n\n // Only instrument flows with supported process types\n if (!FlowInstrumentationService.isSupportedProcessType(flowObj)) {\n this.logger.debug(`Skipping unsupported flow type: ${flowName} (processType=${flowObj?.Flow?.processType || 'undefined'})`);\n return;\n }\n\n // Check if flow already has RFLIB logging and skip if needed\n if (skipInstrumented && FlowInstrumentationService.hasRFLIBLogger(flowObj)) {\n this.logger.info(`Skipping already instrumented flow: ${flowName}`);\n return;\n }\n\n const instrumentedFlow = FlowInstrumentationService.instrumentFlow(flowObj, flowName, skipInstrumented);\n const newContent = FlowInstrumentationService.buildFlowContent(instrumentedFlow);\n\n if (content !== newContent) {\n this.stats.modifiedFiles++;\n if (!isDryRun) {\n await fs.promises.writeFile(filePath, newContent);\n this.logger.info(`Modified: ${filePath}`);\n } else {\n this.logger.info(`Would modify: ${filePath}`);\n }\n }\n } catch (error) {\n this.logger.error(`Error processing flow ${flowName}`, error);\n throw error;\n }\n }\n}"]}
|
|
@@ -4,7 +4,7 @@ Adds RFLIB logging statements to Salesforce Flows.
|
|
|
4
4
|
|
|
5
5
|
# description
|
|
6
6
|
|
|
7
|
-
Automatically adds RFLIB logging statements to Salesforce Flows to provide enhanced tracking and debugging capabilities. Instruments flow invocations and decision paths with logging actions.
|
|
7
|
+
Automatically adds RFLIB logging statements to Salesforce Flows to provide enhanced tracking and debugging capabilities. Works with both standard Flows and Auto-Launched Flows. Instruments flow invocations and decision paths with logging actions. Also sets the CanvasMode to AUTO_LAYOUT_CANVAS for better flow visualization while preserving the original processType.
|
|
8
8
|
|
|
9
9
|
# flags.sourcepath.summary
|
|
10
10
|
|
|
@@ -12,7 +12,7 @@ Directory containing Flow files to instrument with logging.
|
|
|
12
12
|
|
|
13
13
|
# flags.sourcepath.description
|
|
14
14
|
|
|
15
|
-
Path to the source directory containing Flow files that should be instrumented with RFLIB logging statements.
|
|
15
|
+
Path to the source directory containing Flow files that should be instrumented with RFLIB logging statements. Processes .flow-meta.xml files with processType="Flow" or "AutoLaunchedFlow".
|
|
16
16
|
|
|
17
17
|
# flags.dryrun.summary
|
|
18
18
|
|