rflib-plugin 0.7.3 → 0.8.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 CHANGED
@@ -9,6 +9,7 @@ Plugin for Salesforce CLI to help with the adoption of [RFLIB](https://github.co
9
9
  - Automatically instruments Apex classes with RFLIB logging statements
10
10
  - Automatically instruments LWC components with RFLIB logging statements
11
11
  - Automatically instruments Aura components with RFLIB logging statements
12
+ - Automatically instruments Salesforce Flows with RFLIB logging actions
12
13
 
13
14
  ## Installation
14
15
 
@@ -73,7 +74,7 @@ sf rflib logging lwc instrument --sourcepath force-app --skip-instrumented
73
74
  Adds RFLIB logging statements to Aura Components.
74
75
 
75
76
  ```bash
76
- # Add logging to all LWC files
77
+ # Add logging to all Aura component files
77
78
  sf rflib logging aura instrument --sourcepath force-app
78
79
 
79
80
  # Preview changes without modifying files
@@ -93,6 +94,27 @@ sf rflib logging aura instrument --sourcepath force-app --skip-instrumented
93
94
  - `--prettier (-p)`: Format modified files using Prettier
94
95
  - `--skip-instrumented`: Do not instrument files where RFLIB logging is already present
95
96
 
97
+ ### `sf rflib logging flow instrument`
98
+
99
+ Adds RFLIB logging actions to Salesforce Flows.
100
+
101
+ ```bash
102
+ # Add logging to all Flow files
103
+ sf rflib logging flow instrument --sourcepath force-app
104
+
105
+ # Preview changes without modifying files
106
+ sf rflib logging flow instrument --sourcepath force-app --dryrun
107
+
108
+ # Skip instrumenting flows where logging is already present
109
+ sf rflib logging flow instrument --sourcepath force-app --skip-instrumented
110
+ ```
111
+
112
+ #### Command Options
113
+
114
+ - `--sourcepath (-s)`: Directory containing Flow files to instrument
115
+ - `--dryrun (-d)`: Preview changes without modifying files
116
+ - `--skip-instrumented`: Do not instrument files where RFLIB logging is already present
117
+
96
118
  ## Contributing
97
119
 
98
120
  1. Fork the repository
@@ -247,8 +247,24 @@ export default class RflibLoggingApexInstrument extends SfCommand {
247
247
  if (content !== originalContent) {
248
248
  this.stats.modifiedFiles++;
249
249
  if (!isDryRun) {
250
- await fs.promises.writeFile(filePath, content);
251
- this.logger.info(`Modified test file: ${filePath}`);
250
+ try {
251
+ const finalContent = instrumentationOpts.prettier
252
+ ? await ApexInstrumentationService.formatContent(content)
253
+ : content;
254
+ await fs.promises.writeFile(filePath, finalContent);
255
+ if (instrumentationOpts.prettier) {
256
+ this.stats.formattedFiles++;
257
+ this.logger.info(`Modified and formatted test file: ${filePath}`);
258
+ }
259
+ else {
260
+ this.logger.info(`Modified test file: ${filePath}`);
261
+ }
262
+ }
263
+ catch (error) {
264
+ this.logger.warn(`Failed to format ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
265
+ await fs.promises.writeFile(filePath, content);
266
+ this.logger.info(`Modified test file without formatting: ${filePath}`);
267
+ }
252
268
  }
253
269
  else {
254
270
  this.logger.info(`Would modify test file: ${filePath}`);
@@ -1 +1 @@
1
- {"version":3,"file":"instrument.js","sourceRoot":"","sources":["../../../../../src/commands/rflib/logging/apex/instrument.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable no-await-in-loop */\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 prettier from 'prettier';\n\ntype ApexMethodMatch = {\n auraEnabled?: string;\n access: string;\n isStatic?: string;\n returnType: string;\n methodName: string;\n args: string;\n}\n\ntype IfCondition = {\n condition: string;\n position: number;\n}\n\ntype InstrumentationOptions = {\n readonly prettier: boolean;\n readonly noIf: boolean;\n readonly skipInstrumented: boolean;\n}\n\ntype LoggerInfo = {\n readonly exists: boolean;\n readonly variableName: string;\n}\n\ntype ProcessedParameters = {\n readonly paramList: readonly string[];\n readonly logArgs: string;\n}\n\nexport type RflibLoggingApexInstrumentResult = {\n processedFiles: number;\n modifiedFiles: number;\n formattedFiles: number;\n}\n\nMessages.importMessagesDirectoryFromMetaUrl(import.meta.url);\nconst messages = Messages.loadMessages('rflib-plugin', 'rflib.logging.apex.instrument');\n\nclass ApexInstrumentationService {\n public static readonly TEST_SETUP_REGEX =\n /@TestSetup\\s+((public|private|protected|global)s+)?(?:static\\s+)?void\\s+(\\w+)\\s*\\([^)]*\\)\\s*{/g;\n\n private static readonly METHOD_REGEX =\n /(@AuraEnabled\\s*[\\s\\S]*?)?\\b(public|private|protected|global)\\s+(static\\s+)?(?:(\\w+(?:\\s*<(?:[^<>]|<[^<>]*>)*>)?)|void)\\s+(\\w+)\\s*\\(([\\s\\S]*?)\\)\\s*{/g;\n private static readonly CLASS_REGEX = /\\bclass\\s+\\w+\\s*{/;\n private static readonly CLASS_LOGGER_REGEX = /\\bprivate\\s+(?:static\\s+)?(?:final\\s+)?rflib_Logger\\s+(\\w+)\\b/;\n private static readonly GENERIC_ARGS_REGEX = /<[^>]+>/g;\n private static readonly CATCH_REGEX = /catch\\s*\\(\\s*\\w+\\s+(\\w+)\\s*\\)\\s*{/g;\n private static readonly IF_STATEMENT_REGEX =\n /if\\s*\\((.*?)\\)\\s*(?:{([^]*?(?:(?<!{){(?:[^]*?)}(?!})[^]*?)*)}|([^{].*?)(?=\\s*(?:;|$));)/g;\n private static readonly ELSE_REGEX = /\\s*else(?!\\s*if\\b)\\s*(?:{((?:[^{}]|{(?:[^{}]|{[^{}]*})*})*)}|([^{;]*(?:;|$)))/g;\n private static readonly IS_INSTRUMENTED_REGEX = /(\\brflib_Logger\\b|\\brflib_TestUtil\\b)/;\n private static readonly SYSTEM_DEBUG_REGEX = /System\\.debug\\s*\\(\\s*['\"]([^'\"]*)['\"]\\s*\\)\\s*;/g;\n\n private static readonly PRIMITIVE_TYPES = new Set([\n 'STRING',\n 'INTEGER',\n 'LONG',\n 'DECIMAL',\n 'DOUBLE',\n 'BOOLEAN',\n 'DATE',\n 'DATETIME',\n 'TIME',\n 'ID',\n ]);\n\n private static readonly PRETTIER_CONFIG: prettier.Options = {\n parser: 'apex',\n plugins: ['prettier-plugin-apex'],\n printWidth: 120,\n tabWidth: 4,\n useTabs: false,\n singleQuote: true,\n };\n\n public static async formatContent(content: string): Promise<string> {\n try {\n return await prettier.format(content, this.PRETTIER_CONFIG);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Formatting failed: ${error.message}`);\n }\n throw new Error('Formatting failed with unknown error');\n }\n }\n\n public static isInstrumented(content: string): boolean {\n return this.IS_INSTRUMENTED_REGEX.test(content);\n }\n\n public static detectLogger(content: string): LoggerInfo {\n const match = content.match(this.CLASS_LOGGER_REGEX);\n return {\n exists: this.CLASS_LOGGER_REGEX.test(content),\n variableName: match ? match[1] : 'LOGGER',\n };\n }\n\n public static addLoggerDeclaration(content: string, className: string): string {\n const { exists, variableName } = this.detectLogger(content);\n if (!exists) {\n const loggerDecl = `private static final rflib_Logger ${variableName} = rflib_LoggerUtil.getFactory().createLogger('${className}');`;\n return content.replace(this.CLASS_REGEX, `$&\\n ${loggerDecl}`);\n }\n return content;\n }\n\n public static processMethodDeclarations(content: string, loggerName: string): string {\n return content.replace(this.METHOD_REGEX, (match: string, ...args: unknown[]) => {\n const methodInfo: ApexMethodMatch = {\n auraEnabled: args[0] as string,\n access: args[1] as string,\n isStatic: args[2] as string,\n returnType: args[3] as string,\n methodName: args[4] as string,\n args: args[5] as string,\n };\n\n const { paramList, logArgs } = this.processParameters(methodInfo.args);\n\n return `${match}\\n ${loggerName}.info('${methodInfo.methodName}(${paramList\n .map((_, i) => `{${i}}`)\n .join(', ')})'${logArgs});\\n`;\n });\n }\n\n public static processCatchBlocks(content: string, loggerName: string): string {\n return content.replace(this.CATCH_REGEX, (match: string, exceptionVar: string, offset: number) => {\n const contentBeforeCatch = content.substring(0, offset);\n const methodMatches = [...contentBeforeCatch.matchAll(this.METHOD_REGEX)];\n const lastMethodMatch = methodMatches[methodMatches.length - 1];\n const methodName = lastMethodMatch ? lastMethodMatch[5] : 'unknown';\n\n return `${match}\\n ${loggerName}.error('An error occurred in ${methodName}()', ${exceptionVar.trim()});`;\n });\n }\n\n public static processIfStatements(content: string, loggerName: string): string {\n const conditions: IfCondition[] = [];\n\n let modified = content.replace(\n this.IF_STATEMENT_REGEX,\n (match: string, condition: string, blockBody: string, singleLineBody: string, offset: number) => {\n const cleanedUpCondition = condition.trim().replaceAll(\"'\", \"\\\\'\");\n conditions.push({\n condition: cleanedUpCondition,\n position: offset,\n });\n\n const logStatement = `${loggerName}.debug('if (${cleanedUpCondition})');\\n `;\n\n if (blockBody) {\n return `if (${condition}) {\\n ${logStatement}${blockBody}}\\n`;\n } else if (singleLineBody) {\n const cleanBody = singleLineBody.replace(/;$/, '').trim();\n return `if (${condition}) {\\n ${logStatement}${cleanBody};\\n }\\n`;\n }\n return match;\n },\n );\n\n modified = modified.replace(\n this.ELSE_REGEX,\n (match: string, blockBody?: string, singleLineBody?: string, offset?: number) => {\n const nearestIf = conditions\n .filter((c) => c.position < (offset ?? 0))\n .reduce((prev, curr) => (!prev || curr.position > prev.position ? curr : prev));\n\n const logStatement = nearestIf\n ? `${loggerName}.debug('else for if (${nearestIf.condition})');\\n `\n : `${loggerName}.debug('else statement');\\n `;\n\n if (blockBody) {\n return ` else {\\n ${logStatement}${blockBody}}`;\n } else if (singleLineBody) {\n return ` else {\\n ${logStatement}${singleLineBody}\\n }`;\n }\n return match;\n },\n );\n\n return modified;\n }\n\n public static processSystemDebugStatements(content: string, loggerName: string): string {\n return content.replace(this.SYSTEM_DEBUG_REGEX, (match, debugMessage) => `${loggerName}.debug('${debugMessage}');`);\n }\n\n private static isComplexType(paramType: string): boolean {\n return (\n paramType.includes('<') ||\n paramType.includes('[') ||\n paramType === 'Object' ||\n !this.PRIMITIVE_TYPES.has(paramType.toUpperCase())\n );\n }\n\n private static processParameters(args: string): ProcessedParameters {\n const parameters = args\n ? args\n .replaceAll(this.GENERIC_ARGS_REGEX, '')\n .split(',')\n .map((param) => param.trim())\n : [];\n\n const logArgs =\n parameters.length > 0 && parameters[0] !== ''\n ? `, new Object[] { ${parameters\n .map((p) => {\n const [paramType, ...rest] = p.split(' ');\n const paramName = rest.length > 0 ? rest.join(' ') : paramType;\n return this.isComplexType(paramType) ? `JSON.serialize(${paramName})` : paramName;\n })\n .join(', ')} }`\n : '';\n\n return { paramList: parameters, logArgs };\n }\n}\n\nexport default class RflibLoggingApexInstrument extends SfCommand<RflibLoggingApexInstrumentResult> {\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 prettier: Flags.boolean({\n summary: messages.getMessage('flags.prettier.summary'),\n description: messages.getMessage('flags.prettier.description'),\n char: 'p',\n default: false,\n }),\n 'no-if': Flags.boolean({\n summary: messages.getMessage('flags.no-if.summary'),\n description: messages.getMessage('flags.no-if.description'),\n default: false,\n }),\n 'skip-instrumented': Flags.boolean({\n summary: messages.getMessage('flags.skip-instrumented.summary'),\n description: messages.getMessage('flags.skip-instrumented.description'),\n default: false,\n }),\n };\n\n private logger!: Logger;\n private readonly stats: RflibLoggingApexInstrumentResult = {\n processedFiles: 0,\n modifiedFiles: 0,\n formattedFiles: 0,\n };\n\n public async run(): Promise<RflibLoggingApexInstrumentResult> {\n this.logger = await Logger.child(this.ctor.name);\n const startTime = Date.now();\n\n const { flags } = await this.parse(RflibLoggingApexInstrument);\n const sourcePath = flags.sourcepath;\n const isDryRun = flags.dryrun;\n\n const instrumentationOpts: InstrumentationOptions = {\n prettier: flags.prettier,\n noIf: flags['no-if'],\n skipInstrumented: flags['skip-instrumented'],\n };\n\n this.log(`Scanning Apex classes in ${sourcePath} and sub directories`);\n this.logger.debug(`Dry run mode: ${isDryRun}`);\n\n this.spinner.start('Running...');\n await this.processDirectory(sourcePath, isDryRun, instrumentationOpts);\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 this.log(`Formatted files: ${this.stats.formattedFiles}`);\n\n return { ...this.stats };\n }\n\n private async processDirectory(\n dirPath: string,\n isDryRun: boolean,\n instrumentationOpts: InstrumentationOptions,\n ): 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, instrumentationOpts);\n } else if (file.includes('Test') && file.endsWith('.cls')) {\n await this.processTestFile(filePath, isDryRun, instrumentationOpts);\n } else if (file.endsWith('.cls')) {\n await this.instrumentApexClass(filePath, isDryRun, instrumentationOpts);\n }\n }\n }\n\n private async processTestFile(\n filePath: string,\n isDryRun: boolean,\n instrumentationOpts: InstrumentationOptions,\n ): Promise<void> {\n this.logger.debug(`Processing test file: ${filePath}`);\n let content = await fs.promises.readFile(filePath, 'utf8');\n const originalContent = content;\n\n if (instrumentationOpts.skipInstrumented && ApexInstrumentationService.isInstrumented(content)) {\n this.logger.info(`Skipping instrumented test class: ${filePath}`);\n return;\n }\n\n content = content.replace(\n ApexInstrumentationService.TEST_SETUP_REGEX,\n (match) => `${match}\\n rflib_TestUtil.prepareLoggerForUnitTests();`,\n );\n\n if (content !== originalContent) {\n this.stats.modifiedFiles++;\n if (!isDryRun) {\n await fs.promises.writeFile(filePath, content);\n this.logger.info(`Modified test file: ${filePath}`);\n } else {\n this.logger.info(`Would modify test file: ${filePath}`);\n }\n }\n }\n\n private async instrumentApexClass(\n filePath: string,\n isDryRun: boolean,\n instrumentationOpts: InstrumentationOptions,\n ): Promise<void> {\n const className = path.basename(filePath, '.cls');\n this.logger.debug(`Processing class: ${className}`);\n\n try {\n this.stats.processedFiles++;\n let content = await fs.promises.readFile(filePath, 'utf8');\n const originalContent = content;\n\n if (instrumentationOpts.skipInstrumented && ApexInstrumentationService.isInstrumented(content)) {\n this.logger.info(`Skipping instrumented class: ${className}`);\n return;\n }\n\n const { variableName } = ApexInstrumentationService.detectLogger(content);\n content = ApexInstrumentationService.addLoggerDeclaration(content, className);\n content = ApexInstrumentationService.processMethodDeclarations(content, variableName);\n content = ApexInstrumentationService.processSystemDebugStatements(content, variableName);\n content = ApexInstrumentationService.processCatchBlocks(content, variableName);\n\n if (!instrumentationOpts.noIf) {\n content = ApexInstrumentationService.processIfStatements(content, variableName);\n }\n\n if (content !== originalContent) {\n this.stats.modifiedFiles++;\n if (!isDryRun) {\n try {\n const finalContent = instrumentationOpts.prettier\n ? await ApexInstrumentationService.formatContent(content)\n : content;\n\n await fs.promises.writeFile(filePath, finalContent);\n\n if (instrumentationOpts.prettier) {\n this.stats.formattedFiles++;\n this.logger.info(`Modified and formatted: ${filePath}`);\n } else {\n this.logger.info(`Modified: ${filePath}`);\n }\n } catch (error) {\n this.logger.warn(`Failed to format ${filePath}: ${error instanceof Error ? error.message : String(error)}`);\n await fs.promises.writeFile(filePath, content);\n this.logger.info(`Modified without formatting: ${filePath}`);\n }\n } else {\n this.logger.info(`Would modify: ${filePath}`);\n }\n }\n } catch (error) {\n this.logger.error(`Error processing class ${className}`, error);\n throw error;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"instrument.js","sourceRoot":"","sources":["../../../../../src/commands/rflib/logging/apex/instrument.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable no-await-in-loop */\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 prettier from 'prettier';\n\ntype ApexMethodMatch = {\n auraEnabled?: string;\n access: string;\n isStatic?: string;\n returnType: string;\n methodName: string;\n args: string;\n}\n\ntype IfCondition = {\n condition: string;\n position: number;\n}\n\ntype InstrumentationOptions = {\n readonly prettier: boolean;\n readonly noIf: boolean;\n readonly skipInstrumented: boolean;\n}\n\ntype LoggerInfo = {\n readonly exists: boolean;\n readonly variableName: string;\n}\n\ntype ProcessedParameters = {\n readonly paramList: readonly string[];\n readonly logArgs: string;\n}\n\nexport type RflibLoggingApexInstrumentResult = {\n processedFiles: number;\n modifiedFiles: number;\n formattedFiles: number;\n}\n\nMessages.importMessagesDirectoryFromMetaUrl(import.meta.url);\nconst messages = Messages.loadMessages('rflib-plugin', 'rflib.logging.apex.instrument');\n\nclass ApexInstrumentationService {\n public static readonly TEST_SETUP_REGEX =\n /@TestSetup\\s+((public|private|protected|global)s+)?(?:static\\s+)?void\\s+(\\w+)\\s*\\([^)]*\\)\\s*{/g;\n\n private static readonly METHOD_REGEX =\n /(@AuraEnabled\\s*[\\s\\S]*?)?\\b(public|private|protected|global)\\s+(static\\s+)?(?:(\\w+(?:\\s*<(?:[^<>]|<[^<>]*>)*>)?)|void)\\s+(\\w+)\\s*\\(([\\s\\S]*?)\\)\\s*{/g;\n private static readonly CLASS_REGEX = /\\bclass\\s+\\w+\\s*{/;\n private static readonly CLASS_LOGGER_REGEX = /\\bprivate\\s+(?:static\\s+)?(?:final\\s+)?rflib_Logger\\s+(\\w+)\\b/;\n private static readonly GENERIC_ARGS_REGEX = /<[^>]+>/g;\n private static readonly CATCH_REGEX = /catch\\s*\\(\\s*\\w+\\s+(\\w+)\\s*\\)\\s*{/g;\n private static readonly IF_STATEMENT_REGEX =\n /if\\s*\\((.*?)\\)\\s*(?:{([^]*?(?:(?<!{){(?:[^]*?)}(?!})[^]*?)*)}|([^{].*?)(?=\\s*(?:;|$));)/g;\n private static readonly ELSE_REGEX = /\\s*else(?!\\s*if\\b)\\s*(?:{((?:[^{}]|{(?:[^{}]|{[^{}]*})*})*)}|([^{;]*(?:;|$)))/g;\n private static readonly IS_INSTRUMENTED_REGEX = /(\\brflib_Logger\\b|\\brflib_TestUtil\\b)/;\n private static readonly SYSTEM_DEBUG_REGEX = /System\\.debug\\s*\\(\\s*['\"]([^'\"]*)['\"]\\s*\\)\\s*;/g;\n\n private static readonly PRIMITIVE_TYPES = new Set([\n 'STRING',\n 'INTEGER',\n 'LONG',\n 'DECIMAL',\n 'DOUBLE',\n 'BOOLEAN',\n 'DATE',\n 'DATETIME',\n 'TIME',\n 'ID',\n ]);\n\n private static readonly PRETTIER_CONFIG: prettier.Options = {\n parser: 'apex',\n plugins: ['prettier-plugin-apex'],\n printWidth: 120,\n tabWidth: 4,\n useTabs: false,\n singleQuote: true,\n };\n\n public static async formatContent(content: string): Promise<string> {\n try {\n return await prettier.format(content, this.PRETTIER_CONFIG);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Formatting failed: ${error.message}`);\n }\n throw new Error('Formatting failed with unknown error');\n }\n }\n\n public static isInstrumented(content: string): boolean {\n return this.IS_INSTRUMENTED_REGEX.test(content);\n }\n\n public static detectLogger(content: string): LoggerInfo {\n const match = content.match(this.CLASS_LOGGER_REGEX);\n return {\n exists: this.CLASS_LOGGER_REGEX.test(content),\n variableName: match ? match[1] : 'LOGGER',\n };\n }\n\n public static addLoggerDeclaration(content: string, className: string): string {\n const { exists, variableName } = this.detectLogger(content);\n if (!exists) {\n const loggerDecl = `private static final rflib_Logger ${variableName} = rflib_LoggerUtil.getFactory().createLogger('${className}');`;\n return content.replace(this.CLASS_REGEX, `$&\\n ${loggerDecl}`);\n }\n return content;\n }\n\n public static processMethodDeclarations(content: string, loggerName: string): string {\n return content.replace(this.METHOD_REGEX, (match: string, ...args: unknown[]) => {\n const methodInfo: ApexMethodMatch = {\n auraEnabled: args[0] as string,\n access: args[1] as string,\n isStatic: args[2] as string,\n returnType: args[3] as string,\n methodName: args[4] as string,\n args: args[5] as string,\n };\n\n const { paramList, logArgs } = this.processParameters(methodInfo.args);\n\n return `${match}\\n ${loggerName}.info('${methodInfo.methodName}(${paramList\n .map((_, i) => `{${i}}`)\n .join(', ')})'${logArgs});\\n`;\n });\n }\n\n public static processCatchBlocks(content: string, loggerName: string): string {\n return content.replace(this.CATCH_REGEX, (match: string, exceptionVar: string, offset: number) => {\n const contentBeforeCatch = content.substring(0, offset);\n const methodMatches = [...contentBeforeCatch.matchAll(this.METHOD_REGEX)];\n const lastMethodMatch = methodMatches[methodMatches.length - 1];\n const methodName = lastMethodMatch ? lastMethodMatch[5] : 'unknown';\n\n return `${match}\\n ${loggerName}.error('An error occurred in ${methodName}()', ${exceptionVar.trim()});`;\n });\n }\n\n public static processIfStatements(content: string, loggerName: string): string {\n const conditions: IfCondition[] = [];\n\n let modified = content.replace(\n this.IF_STATEMENT_REGEX,\n (match: string, condition: string, blockBody: string, singleLineBody: string, offset: number) => {\n const cleanedUpCondition = condition.trim().replaceAll(\"'\", \"\\\\'\");\n conditions.push({\n condition: cleanedUpCondition,\n position: offset,\n });\n\n const logStatement = `${loggerName}.debug('if (${cleanedUpCondition})');\\n `;\n\n if (blockBody) {\n return `if (${condition}) {\\n ${logStatement}${blockBody}}\\n`;\n } else if (singleLineBody) {\n const cleanBody = singleLineBody.replace(/;$/, '').trim();\n return `if (${condition}) {\\n ${logStatement}${cleanBody};\\n }\\n`;\n }\n return match;\n },\n );\n\n modified = modified.replace(\n this.ELSE_REGEX,\n (match: string, blockBody?: string, singleLineBody?: string, offset?: number) => {\n const nearestIf = conditions\n .filter((c) => c.position < (offset ?? 0))\n .reduce((prev, curr) => (!prev || curr.position > prev.position ? curr : prev));\n\n const logStatement = nearestIf\n ? `${loggerName}.debug('else for if (${nearestIf.condition})');\\n `\n : `${loggerName}.debug('else statement');\\n `;\n\n if (blockBody) {\n return ` else {\\n ${logStatement}${blockBody}}`;\n } else if (singleLineBody) {\n return ` else {\\n ${logStatement}${singleLineBody}\\n }`;\n }\n return match;\n },\n );\n\n return modified;\n }\n\n public static processSystemDebugStatements(content: string, loggerName: string): string {\n return content.replace(this.SYSTEM_DEBUG_REGEX, (match, debugMessage) => `${loggerName}.debug('${debugMessage}');`);\n }\n\n private static isComplexType(paramType: string): boolean {\n return (\n paramType.includes('<') ||\n paramType.includes('[') ||\n paramType === 'Object' ||\n !this.PRIMITIVE_TYPES.has(paramType.toUpperCase())\n );\n }\n\n private static processParameters(args: string): ProcessedParameters {\n const parameters = args\n ? args\n .replaceAll(this.GENERIC_ARGS_REGEX, '')\n .split(',')\n .map((param) => param.trim())\n : [];\n\n const logArgs =\n parameters.length > 0 && parameters[0] !== ''\n ? `, new Object[] { ${parameters\n .map((p) => {\n const [paramType, ...rest] = p.split(' ');\n const paramName = rest.length > 0 ? rest.join(' ') : paramType;\n return this.isComplexType(paramType) ? `JSON.serialize(${paramName})` : paramName;\n })\n .join(', ')} }`\n : '';\n\n return { paramList: parameters, logArgs };\n }\n}\n\nexport default class RflibLoggingApexInstrument extends SfCommand<RflibLoggingApexInstrumentResult> {\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 prettier: Flags.boolean({\n summary: messages.getMessage('flags.prettier.summary'),\n description: messages.getMessage('flags.prettier.description'),\n char: 'p',\n default: false,\n }),\n 'no-if': Flags.boolean({\n summary: messages.getMessage('flags.no-if.summary'),\n description: messages.getMessage('flags.no-if.description'),\n default: false,\n }),\n 'skip-instrumented': Flags.boolean({\n summary: messages.getMessage('flags.skip-instrumented.summary'),\n description: messages.getMessage('flags.skip-instrumented.description'),\n default: false,\n }),\n };\n\n private logger!: Logger;\n private readonly stats: RflibLoggingApexInstrumentResult = {\n processedFiles: 0,\n modifiedFiles: 0,\n formattedFiles: 0,\n };\n\n public async run(): Promise<RflibLoggingApexInstrumentResult> {\n this.logger = await Logger.child(this.ctor.name);\n const startTime = Date.now();\n\n const { flags } = await this.parse(RflibLoggingApexInstrument);\n const sourcePath = flags.sourcepath;\n const isDryRun = flags.dryrun;\n\n const instrumentationOpts: InstrumentationOptions = {\n prettier: flags.prettier,\n noIf: flags['no-if'],\n skipInstrumented: flags['skip-instrumented'],\n };\n\n this.log(`Scanning Apex classes in ${sourcePath} and sub directories`);\n this.logger.debug(`Dry run mode: ${isDryRun}`);\n\n this.spinner.start('Running...');\n await this.processDirectory(sourcePath, isDryRun, instrumentationOpts);\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 this.log(`Formatted files: ${this.stats.formattedFiles}`);\n\n return { ...this.stats };\n }\n\n private async processDirectory(\n dirPath: string,\n isDryRun: boolean,\n instrumentationOpts: InstrumentationOptions,\n ): 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, instrumentationOpts);\n } else if (file.includes('Test') && file.endsWith('.cls')) {\n await this.processTestFile(filePath, isDryRun, instrumentationOpts);\n } else if (file.endsWith('.cls')) {\n await this.instrumentApexClass(filePath, isDryRun, instrumentationOpts);\n }\n }\n }\n\n private async processTestFile(\n filePath: string,\n isDryRun: boolean,\n instrumentationOpts: InstrumentationOptions,\n ): Promise<void> {\n this.logger.debug(`Processing test file: ${filePath}`);\n let content = await fs.promises.readFile(filePath, 'utf8');\n const originalContent = content;\n\n if (instrumentationOpts.skipInstrumented && ApexInstrumentationService.isInstrumented(content)) {\n this.logger.info(`Skipping instrumented test class: ${filePath}`);\n return;\n }\n\n content = content.replace(\n ApexInstrumentationService.TEST_SETUP_REGEX,\n (match) => `${match}\\n rflib_TestUtil.prepareLoggerForUnitTests();`,\n );\n\n if (content !== originalContent) {\n this.stats.modifiedFiles++;\n if (!isDryRun) {\n try {\n const finalContent = instrumentationOpts.prettier\n ? await ApexInstrumentationService.formatContent(content)\n : content;\n\n await fs.promises.writeFile(filePath, finalContent);\n\n if (instrumentationOpts.prettier) {\n this.stats.formattedFiles++;\n this.logger.info(`Modified and formatted test file: ${filePath}`);\n } else {\n this.logger.info(`Modified test file: ${filePath}`);\n }\n } catch (error) {\n this.logger.warn(`Failed to format ${filePath}: ${error instanceof Error ? error.message : String(error)}`);\n await fs.promises.writeFile(filePath, content);\n this.logger.info(`Modified test file without formatting: ${filePath}`);\n }\n } else {\n this.logger.info(`Would modify test file: ${filePath}`);\n }\n }\n }\n\n private async instrumentApexClass(\n filePath: string,\n isDryRun: boolean,\n instrumentationOpts: InstrumentationOptions,\n ): Promise<void> {\n const className = path.basename(filePath, '.cls');\n this.logger.debug(`Processing class: ${className}`);\n\n try {\n this.stats.processedFiles++;\n let content = await fs.promises.readFile(filePath, 'utf8');\n const originalContent = content;\n\n if (instrumentationOpts.skipInstrumented && ApexInstrumentationService.isInstrumented(content)) {\n this.logger.info(`Skipping instrumented class: ${className}`);\n return;\n }\n\n const { variableName } = ApexInstrumentationService.detectLogger(content);\n content = ApexInstrumentationService.addLoggerDeclaration(content, className);\n content = ApexInstrumentationService.processMethodDeclarations(content, variableName);\n content = ApexInstrumentationService.processSystemDebugStatements(content, variableName);\n content = ApexInstrumentationService.processCatchBlocks(content, variableName);\n\n if (!instrumentationOpts.noIf) {\n content = ApexInstrumentationService.processIfStatements(content, variableName);\n }\n\n if (content !== originalContent) {\n this.stats.modifiedFiles++;\n if (!isDryRun) {\n try {\n const finalContent = instrumentationOpts.prettier\n ? await ApexInstrumentationService.formatContent(content)\n : content;\n\n await fs.promises.writeFile(filePath, finalContent);\n\n if (instrumentationOpts.prettier) {\n this.stats.formattedFiles++;\n this.logger.info(`Modified and formatted: ${filePath}`);\n } else {\n this.logger.info(`Modified: ${filePath}`);\n }\n } catch (error) {\n this.logger.warn(`Failed to format ${filePath}: ${error instanceof Error ? error.message : String(error)}`);\n await fs.promises.writeFile(filePath, content);\n this.logger.info(`Modified without formatting: ${filePath}`);\n }\n } else {\n this.logger.info(`Would modify: ${filePath}`);\n }\n }\n } catch (error) {\n this.logger.error(`Error processing class ${className}`, error);\n throw error;\n }\n }\n}\n"]}
@@ -10,8 +10,13 @@ export declare class FlowInstrumentationService {
10
10
  static buildFlowContent(flowObj: any): string;
11
11
  static hasRFLIBLogger(flowObj: any): boolean;
12
12
  static isFlowType(flowObj: any): boolean;
13
- static instrumentFlow(flowObj: any, flowName: string): any;
13
+ static instrumentFlow(flowObj: any, flowName: string, skipInstrumented?: boolean): any;
14
+ private static instrumentDecisions;
15
+ private static addActionCallToFlow;
16
+ private static createDecisionPathLogger;
14
17
  private static generateUniqueId;
18
+ private static sanitizeForName;
19
+ private static truncateLabel;
15
20
  private static createLoggingAction;
16
21
  private static enhanceLoggingWithVariables;
17
22
  }
@@ -59,16 +59,16 @@ export class FlowInstrumentationService {
59
59
  action.actionName === 'rflib_ApplicationEventLoggerAction' ||
60
60
  (action.name && typeof action.name === 'string' && action.name.startsWith('RFLIB_Flow_Logger')));
61
61
  }
62
- // Helper to check if flow is of type "Flow" that we want to instrument
62
+ // Helper to check if flow is of type that we want to instrument
63
63
  static isFlowType(flowObj) {
64
64
  return flowObj?.Flow?.processType === 'Flow';
65
65
  }
66
66
  // Main instrumentation function
67
- static instrumentFlow(flowObj, flowName) {
67
+ static instrumentFlow(flowObj, flowName, skipInstrumented = false) {
68
68
  // Deep clone the object to avoid modifying the original
69
69
  const instrumentedFlow = JSON.parse(JSON.stringify(flowObj));
70
- // Skip if already instrumented
71
- if (this.hasRFLIBLogger(instrumentedFlow)) {
70
+ // Skip if already instrumented and skipInstrumented flag is set
71
+ if (skipInstrumented && this.hasRFLIBLogger(instrumentedFlow)) {
72
72
  return instrumentedFlow;
73
73
  }
74
74
  // Make sure Flow exists in the object
@@ -134,20 +134,275 @@ export class FlowInstrumentationService {
134
134
  else if (!Array.isArray(instrumentedFlow.Flow.variables)) {
135
135
  instrumentedFlow.Flow.variables = [instrumentedFlow.Flow.variables];
136
136
  }
137
+ // Instrument decisions with logging for each outcome
138
+ if (instrumentedFlow.Flow.decisions) {
139
+ this.instrumentDecisions(instrumentedFlow, flowName);
140
+ }
137
141
  return instrumentedFlow;
138
142
  }
139
- // Helper to generate unique IDs for new flow elements
143
+ // Helper to instrument decision paths with logging
144
+ static instrumentDecisions(flowObj, flowName) {
145
+ if (!flowObj.Flow.decisions) {
146
+ return;
147
+ }
148
+ // Convert to array if there's only one decision
149
+ const decisions = Array.isArray(flowObj.Flow.decisions)
150
+ ? flowObj.Flow.decisions
151
+ : [flowObj.Flow.decisions];
152
+ // Process each decision
153
+ decisions.forEach((decision) => {
154
+ // Skip if decision doesn't have a name
155
+ if (!decision.name) {
156
+ return;
157
+ }
158
+ const decisionName = decision.name;
159
+ const decisionLabel = decision.label || decisionName;
160
+ // Process default connector if it exists
161
+ if (decision.defaultConnector?.targetReference) {
162
+ const defaultTarget = decision.defaultConnector.targetReference;
163
+ const defaultConnectorLabel = decision.defaultConnectorLabel || 'Default Outcome';
164
+ // Create a logger for the default path
165
+ const defaultLogger = this.createDecisionPathLogger(flowName, String(decisionName), String(decisionLabel), 'default', String(defaultConnectorLabel));
166
+ // Connect logger to the original target
167
+ defaultLogger.connector = {
168
+ targetReference: defaultTarget
169
+ };
170
+ // Add logger to actionCalls first, before updating the decision connector
171
+ this.addActionCallToFlow(flowObj, defaultLogger);
172
+ // Update the decision's default connector to point to our logger
173
+ // We're inside a forEach callback, so we have to modify the original object
174
+ /* eslint-disable no-param-reassign */
175
+ decision.defaultConnector.targetReference = defaultLogger.name;
176
+ /* eslint-enable no-param-reassign */
177
+ }
178
+ // Process each rule if they exist
179
+ if (decision.rules) {
180
+ const rules = Array.isArray(decision.rules) ? decision.rules : [decision.rules];
181
+ rules.forEach((rule) => {
182
+ // Skip if rule doesn't have a connector or name
183
+ if (!rule.connector?.targetReference || !rule.name) {
184
+ return;
185
+ }
186
+ const ruleTarget = rule.connector.targetReference;
187
+ const ruleName = rule.name;
188
+ const ruleLabel = rule.label || ruleName;
189
+ // Create a logger for this rule outcome
190
+ const ruleLogger = this.createDecisionPathLogger(flowName, String(decisionName), String(decisionLabel), String(ruleName), String(ruleLabel));
191
+ // Connect logger to the original target
192
+ ruleLogger.connector = {
193
+ targetReference: ruleTarget
194
+ };
195
+ // Add logger to actionCalls first, before updating the rule connector
196
+ this.addActionCallToFlow(flowObj, ruleLogger);
197
+ // Update the rule's connector to point to our logger
198
+ // We're inside a forEach callback, so we have to modify the original object
199
+ /* eslint-disable no-param-reassign */
200
+ rule.connector.targetReference = ruleLogger.name;
201
+ /* eslint-enable no-param-reassign */
202
+ });
203
+ }
204
+ });
205
+ }
206
+ // Helper to add action calls to the flow object
207
+ // Note: This method does modify the parameter directly - we accepted the eslint warning
208
+ // since we need to modify the flow object within callback functions where returning a new value isn't possible
209
+ static addActionCallToFlow(flowObj, actionCall) {
210
+ /* eslint-disable no-param-reassign */
211
+ if (!flowObj.Flow.actionCalls) {
212
+ flowObj.Flow.actionCalls = actionCall;
213
+ }
214
+ else if (Array.isArray(flowObj.Flow.actionCalls)) {
215
+ flowObj.Flow.actionCalls.push(actionCall);
216
+ }
217
+ else {
218
+ // If only one action exists, convert to array
219
+ flowObj.Flow.actionCalls = [flowObj.Flow.actionCalls, actionCall];
220
+ }
221
+ /* eslint-enable no-param-reassign */
222
+ }
223
+ // Helper to create a logging action for decision paths
224
+ static createDecisionPathLogger(flowName, decisionName, decisionLabel, outcomeName, outcomeLabel) {
225
+ const loggerId = this.generateUniqueId();
226
+ // Ensure we're working with strings
227
+ const decisionNameStr = String(decisionName);
228
+ const outcomeNameStr = String(outcomeName);
229
+ const decisionLabelStr = String(decisionLabel);
230
+ const outcomeLabelStr = String(outcomeLabel);
231
+ // Calculate maximum lengths to stay under 80 characters
232
+ const prefixLength = 'RFLIB_Flow_Logger_Decision_'.length;
233
+ const separatorsLength = 2; // Two underscores
234
+ const maxTotalNameLength = 80 - prefixLength - loggerId.length - separatorsLength;
235
+ // Allocate half of available space to each name (decision and outcome)
236
+ const maxIndividualLength = Math.floor(maxTotalNameLength / 2);
237
+ // Sanitize names to fit Salesforce naming rules
238
+ const sanitizedDecisionName = this.sanitizeForName(decisionNameStr, maxIndividualLength);
239
+ const sanitizedOutcomeName = this.sanitizeForName(outcomeNameStr, maxIndividualLength);
240
+ // Create a name that's guaranteed to be under 80 chars and follow Salesforce rules
241
+ const name = `RFLIB_Flow_Logger_Decision_${sanitizedDecisionName}_${sanitizedOutcomeName}_${loggerId}`;
242
+ // Create and truncate the label to ensure it's under 80 chars
243
+ const label = this.truncateLabel(`Log Decision: ${decisionLabelStr} - ${outcomeLabelStr}`);
244
+ // Fallback if still too long
245
+ if (name.length > 80) {
246
+ return {
247
+ actionName: 'rflib_LoggerFlowAction',
248
+ actionType: 'apex',
249
+ name: `RFLIBLogDec${loggerId}`,
250
+ label,
251
+ locationX: 176,
252
+ locationY: 50,
253
+ inputParameters: [
254
+ {
255
+ name: 'context',
256
+ value: {
257
+ stringValue: flowName,
258
+ },
259
+ },
260
+ {
261
+ name: 'logLevel',
262
+ value: {
263
+ stringValue: 'INFO',
264
+ },
265
+ },
266
+ {
267
+ name: 'message',
268
+ value: {
269
+ stringValue: `Decision '${decisionLabelStr}' outcome: ${outcomeLabelStr}`,
270
+ },
271
+ },
272
+ ],
273
+ };
274
+ }
275
+ return {
276
+ actionName: 'rflib_LoggerFlowAction',
277
+ actionType: 'apex',
278
+ name,
279
+ label,
280
+ locationX: 176,
281
+ locationY: 50,
282
+ inputParameters: [
283
+ {
284
+ name: 'context',
285
+ value: {
286
+ stringValue: flowName,
287
+ },
288
+ },
289
+ {
290
+ name: 'logLevel',
291
+ value: {
292
+ stringValue: 'INFO',
293
+ },
294
+ },
295
+ {
296
+ name: 'message',
297
+ value: {
298
+ stringValue: `Decision '${decisionLabelStr}' outcome: ${outcomeLabelStr}`,
299
+ },
300
+ },
301
+ ],
302
+ };
303
+ }
304
+ // Helper to generate unique IDs for new flow elements (compact for 80-char limit)
305
+ // that follow Salesforce Flow Action naming rules
140
306
  static generateUniqueId() {
141
- return `RFLIB_LOG_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 9)}`;
307
+ // Use timestamp in base36 + 4 random chars to keep it short but unique
308
+ // Ensure we don't start with a number or have consecutive/trailing underscores
309
+ const timestamp = Date.now().toString(36);
310
+ const random = Math.random().toString(36).substring(2, 6);
311
+ // Combine parts without underscore to avoid potential consecutive underscores
312
+ return `ID${timestamp}${random}`;
313
+ }
314
+ // Helper to sanitize and truncate text to fit within the 80-char name limit
315
+ // and to follow Salesforce Flow Action naming rules:
316
+ // - Only alphanumeric characters and underscores
317
+ // - Must begin with a letter
318
+ // - No spaces
319
+ // - No underscore at the end
320
+ // - No consecutive underscores
321
+ static sanitizeForName(text, maxLength) {
322
+ if (!text) {
323
+ return 'X'; // Default to 'X' for empty inputs to ensure we start with a letter
324
+ }
325
+ // First, replace any non-alphanumeric characters with underscores
326
+ let sanitized = text.replace(/[^a-zA-Z0-9_]/g, '_');
327
+ // Ensure it starts with a letter
328
+ if (!/^[a-zA-Z]/.test(sanitized)) {
329
+ sanitized = 'X' + sanitized;
330
+ }
331
+ // Replace consecutive underscores with a single underscore
332
+ sanitized = sanitized.replace(/__+/g, '_');
333
+ // Remove trailing underscore if present
334
+ sanitized = sanitized.replace(/_+$/, '');
335
+ // If empty after sanitization, return a default value
336
+ if (!sanitized) {
337
+ return 'X';
338
+ }
339
+ // Truncate if longer than maxLength
340
+ if (sanitized.length > maxLength) {
341
+ // Truncate and ensure it doesn't end with an underscore
342
+ sanitized = sanitized.substring(0, maxLength).replace(/_+$/, '');
343
+ // If we removed trailing underscores and now it's empty or too short, add a fallback
344
+ if (sanitized.length < 1) {
345
+ sanitized = 'X';
346
+ }
347
+ }
348
+ return sanitized;
349
+ }
350
+ // Helper to truncate label text to fit within the 80-char limit
351
+ static truncateLabel(label, maxLength = 80) {
352
+ if (!label || label.length <= maxLength) {
353
+ return label;
354
+ }
355
+ // If text is too long, truncate it and add ellipsis
356
+ return label.substring(0, maxLength - 3) + '...';
142
357
  }
143
358
  // Helper to create a logging action element
144
359
  static createLoggingAction(flowName) {
145
360
  const loggerId = this.generateUniqueId();
361
+ // Ensure name is under 80 characters and follows Salesforce naming rules
362
+ const prefixLength = 'RFLIB_Flow_Logger_'.length;
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}`;
367
+ // Create and truncate the label to ensure it's under 80 chars
368
+ const label = this.truncateLabel(`Log Flow Invocation: ${flowName}`);
369
+ // Verify name length
370
+ if (name.length > 80) {
371
+ // If still too long, use a simpler naming scheme (fallback)
372
+ return {
373
+ actionName: 'rflib_LoggerFlowAction',
374
+ actionType: 'apex',
375
+ name: `RFLIBLogger${loggerId}`,
376
+ label,
377
+ locationX: 176,
378
+ locationY: 50,
379
+ inputParameters: [
380
+ {
381
+ name: 'context',
382
+ value: {
383
+ stringValue: flowName,
384
+ },
385
+ },
386
+ {
387
+ name: 'logLevel',
388
+ value: {
389
+ stringValue: 'INFO',
390
+ },
391
+ },
392
+ {
393
+ name: 'message',
394
+ value: {
395
+ stringValue: `Flow ${flowName} started`,
396
+ },
397
+ },
398
+ ],
399
+ };
400
+ }
146
401
  return {
147
402
  actionName: 'rflib_LoggerFlowAction',
148
403
  actionType: 'apex',
149
- name: `RFLIB_Flow_Logger_${loggerId}`,
150
- label: 'Log Flow Invocation',
404
+ name,
405
+ label,
151
406
  locationX: 176,
152
407
  locationY: 50,
153
408
  inputParameters: [
@@ -274,7 +529,7 @@ export default class RflibLoggingFlowInstrument extends SfCommand {
274
529
  this.logger.info(`Skipping already instrumented flow: ${flowName}`);
275
530
  return;
276
531
  }
277
- const instrumentedFlow = FlowInstrumentationService.instrumentFlow(flowObj, flowName);
532
+ const instrumentedFlow = FlowInstrumentationService.instrumentFlow(flowObj, flowName, skipInstrumented);
278
533
  const newContent = FlowInstrumentationService.buildFlowContent(instrumentedFlow);
279
534
  if (content !== newContent) {
280
535
  this.stats.modifiedFiles++;
@@ -1 +1 @@
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 */\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 \"Flow\" 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): 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\n if (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 return instrumentedFlow;\n }\n\n // Helper to generate unique IDs for new flow elements\n private static generateUniqueId(): string {\n return `RFLIB_LOG_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 9)}`;\n }\n\n // Helper to create a logging action element\n private static createLoggingAction(flowName: string): any {\n const loggerId = this.generateUniqueId();\n return {\n actionName: 'rflib_LoggerFlowAction',\n actionType: 'apex',\n name: `RFLIB_Flow_Logger_${loggerId}`,\n label: 'Log Flow Invocation',\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);\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}\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 */\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,18 +1,18 @@
1
1
  # summary
2
2
 
3
- Summary of a command.
3
+ Adds RFLIB logging statements to Salesforce Flows.
4
4
 
5
5
  # description
6
6
 
7
- More information about a command. Don't repeat the summary.
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.
8
8
 
9
9
  # flags.sourcepath.summary
10
10
 
11
- Directory containing Apex classes to instrument with logging.
11
+ Directory containing Flow files to instrument with logging.
12
12
 
13
13
  # flags.sourcepath.description
14
14
 
15
- Path to the source directory containing Apex classes that should be instrumented with RFLIB logging statements. Test classes (ending with 'Test.cls') are automatically excluded.
15
+ Path to the source directory containing Flow files that should be instrumented with RFLIB logging statements. Only processes .flow-meta.xml files with processType="Flow".
16
16
 
17
17
  # flags.dryrun.summary
18
18
 
@@ -22,15 +22,25 @@ Preview changes without modifying files.
22
22
 
23
23
  When enabled, shows which files would be modified without making actual changes. Useful for reviewing the impact before applying changes.
24
24
 
25
+ # flags.prettier.summary
26
+
27
+ Format output files with prettier.
28
+
29
+ # flags.prettier.description
30
+
31
+ Use prettier to format the output files.
32
+
25
33
  # flags.skip-instrumented.summary
26
34
 
27
35
  Skips any files where a logger is already present.
28
36
 
29
37
  # flags.skip-instrumented.description
30
38
 
31
- When provided, the command will not add log statements to any Flows that already contains a RFLIB logging node.
39
+ When provided, the command will not add log statements to any Flows that already contain RFLIB logging actions.
32
40
 
33
41
  # examples
34
42
 
35
- - <%= config.bin %> <%= command.id %>
43
+ - <%= config.bin %> <%= command.id %> --sourcepath force-app
44
+ - <%= config.bin %> <%= command.id %> --sourcepath force-app --dryrun
45
+ - <%= config.bin %> <%= command.id %> --sourcepath force-app --skip-instrumented
36
46
 
@@ -223,9 +223,11 @@
223
223
  "rflib:logging:flow:instrument": {
224
224
  "aliases": [],
225
225
  "args": {},
226
- "description": "More information about a command. Don't repeat the summary.",
226
+ "description": "Automatically adds RFLIB logging statements to Salesforce Flows to provide enhanced tracking and debugging capabilities. Instruments flow invocations and decision paths with logging actions.",
227
227
  "examples": [
228
- "<%= config.bin %> <%= command.id %>"
228
+ "<%= config.bin %> <%= command.id %> --sourcepath force-app",
229
+ "<%= config.bin %> <%= command.id %> --sourcepath force-app --dryrun",
230
+ "<%= config.bin %> <%= command.id %> --sourcepath force-app --skip-instrumented"
229
231
  ],
230
232
  "flags": {
231
233
  "json": {
@@ -245,10 +247,10 @@
245
247
  },
246
248
  "sourcepath": {
247
249
  "char": "s",
248
- "description": "Path to the source directory containing Apex classes that should be instrumented with RFLIB logging statements. Test classes (ending with 'Test.cls') are automatically excluded.",
250
+ "description": "Path to the source directory containing Flow files that should be instrumented with RFLIB logging statements. Only processes .flow-meta.xml files with processType=\"Flow\".",
249
251
  "name": "sourcepath",
250
252
  "required": true,
251
- "summary": "Directory containing Apex classes to instrument with logging.",
253
+ "summary": "Directory containing Flow files to instrument with logging.",
252
254
  "hasDynamicHelp": false,
253
255
  "multiple": false,
254
256
  "type": "option"
@@ -262,7 +264,7 @@
262
264
  "type": "boolean"
263
265
  },
264
266
  "skip-instrumented": {
265
- "description": "When provided, the command will not add log statements to any Flows that already contains a RFLIB logging node.",
267
+ "description": "When provided, the command will not add log statements to any Flows that already contain RFLIB logging actions.",
266
268
  "name": "skip-instrumented",
267
269
  "summary": "Skips any files where a logger is already present.",
268
270
  "allowNo": false,
@@ -276,7 +278,7 @@
276
278
  "pluginName": "rflib-plugin",
277
279
  "pluginType": "core",
278
280
  "strict": true,
279
- "summary": "Summary of a command.",
281
+ "summary": "Adds RFLIB logging statements to Salesforce Flows.",
280
282
  "enableJsonFlag": true,
281
283
  "isESM": true,
282
284
  "relativePath": [
@@ -426,5 +428,5 @@
426
428
  ]
427
429
  }
428
430
  },
429
- "version": "0.7.3"
431
+ "version": "0.8.0"
430
432
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rflib-plugin",
3
3
  "description": "Utility commands to help with the adoption of the RFLIB open-source library for Salesforce.",
4
- "version": "0.7.3",
4
+ "version": "0.8.0",
5
5
  "dependencies": {
6
6
  "@oclif/core": "^4",
7
7
  "@salesforce/core": "^8",