npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lightning-flow-scanner

v6.19.1

Published

A Salesforce CLI plugin for analysis and optimization of Salesforce Flow. Scans metadata for 20+ issues such as hardcoded IDs, unsafe contexts, inefficient SOQL/DML operations, recursion risks, and missing fault handling. Supports auto-fixes, rule configu

Readme


Table of contents


Usage

Privacy: Zero user data collected. All processing is client-side. → See our Security Policy.

Scan Command

Lightning Flow Scanner CLI is plug-and-play. Just open any Salesforce project and run:

sf flow:scan

All default rules are applied automatically.

sf flow:scan # Scan flows in the current directory
sf flow:scan --sarif > report.sarif # Export scan results as SARIF
sf flow scan --csv > results.csv # Export scan results as CSV

| Flag | Alias | Description | Example | |-----------------|-------|-----------------------------------------------|-------------------------------------| | --config | -c | Path to config file | -c .flow-scanner.yml | | --directory | -d | Directory to scan | -d force-app | | --files | -p | Specific flow files | -p "flows/MyFlow.flow-meta.xml" | | --csv | -v | CSV output | --csv > violations.csv | | --sarif | -s | SARIF output (GitHub, etc.) | --sarif > report.sarif | | --json | | Pretty JSON output | --json | | --threshold | -t | Fail on error/warn | --threshold error | | --betaMode | -z | Enable beta rules | --betaMode | | --loglevel | | Logging level | --loglevel debug |

Fix Command

The flow:fix command automatically fixes certain issues in your flows. It uses the same .flow-scanner.yml configuration as the scan command.

sf flow:fix -d src/flows                    # Apply fixes using config rules
sf flow:fix -d src/flows --dry-run          # Preview changes without applying
sf flow:fix -d src/flows -i                 # Interactive mode with confirmation
sf flow:fix -d src/flows -r unused-variable # Filter to specific rule

| Flag | Alias | Description | Example | |-----------------|-------|-----------------------------------------------|-------------------------------------| | --config | -c | Path to configuration file | -c .flow-scanner.yml | | --directory | -d | Directory containing flows | -d force-app | | --files | -f | Specific flow files | -f "flows/MyFlow.flow-meta.xml" | | --rules | -r | Filter to specific rules (optional) | -r unused-variable | | --dry-run | | Preview changes without applying | --dry-run | | --interactive| -i | Show diff and prompt for confirmation | -i |

Default Rules

Want to help improve this project? See our Contributing Guidelines


Problems

These rules detect anti-patterns and unsafe practices in your Flows that could break functionality, compromise security, or cause deployment failures.

DML Statement In A Loop

Executing DML operations (insert, update, delete) inside a loop is a high-risk anti-pattern that frequently causes governor limit exceptions. All database operations should be collected and executed once, outside the loop.

Rule ID: dml-in-loop Class Name: DMLStatementInLoop Severity: 🔴 Error

Hardcoded Id

Avoid hard-coding record IDs, as they are unique to a specific org and will not work in other environments. Instead, store IDs in variables—such as merge-field URL parameters or a Get Records element—to make the Flow portable, maintainable, and flexible.

Rule ID: hardcoded-id Class Name: HardcodedId Severity: 🔴 Error

Hardcoded Secret Beta

Avoid hardcoding secrets, API keys, tokens, or credentials in Flows. These should be stored securely in Named Credentials, Custom Settings, Custom Metadata, or external secret management systems.

Rule ID: hardcoded-secret Class Name: HardcodedSecret Severity: 🔴 Error

Hardcoded Url

Avoid hard-coding URLs, as they may change between environments or over time. Instead, store URLs in variables or custom settings to make the Flow adaptable, maintainable, and environment-independent.

Rule ID: hardcoded-url Class Name: HardcodedUrl Severity: 🔴 Error

Process Builder

Process Builder is retired. Continuing to use it increases maintenance overhead and risks future compatibility issues. Migrating automation to Flow reduces risk and improves maintainability.

Rule ID: process-builder-usage Class Name: ProcessBuilder Severity: 🔴 Error

SOQL Query In A Loop

Running SOQL queries inside a loop can rapidly exceed query limits and severely degrade performance. Queries should be executed once, with results reused throughout the loop.

Rule ID: soql-in-loop Class Name: SOQLQueryInLoop Severity: 🔴 Error

Unsafe Running Context

Flows configured to run in System Mode without Sharing grant access to all data, bypassing user permissions. Avoid this setting to prevent security risks and protect sensitive data.

Rule ID: unsafe-running-context Class Name: UnsafeRunningContext Severity: 🔴 Error

Duplicate DML Operation

When a Flow performs database operations across multiple screens, users navigating backward can cause the same actions to run multiple times. To prevent unintended changes, either restrict backward navigation or redesign the Flow so database operations execute in a single, forward-moving step.

Rule ID: duplicate-dml Class Name: DuplicateDMLOperation Severity: 🟡 Warning

Missing Fault Path

Elements that can fail should include a Fault Path to handle errors gracefully. Without it, failures show generic errors to users. Fault Paths improve reliability and user experience.

Rule ID: missing-fault-path Class Name: MissingFaultPath Severity: 🟡 Warning

Missing Null Handler

Get Records operations return null when no data is found. Without handling these null values, Flows can fail or produce unintended results. Adding a null check improves reliability and ensures the Flow behaves as expected.

Rule ID: missing-null-handler Class Name: MissingNullHandler Severity: 🟡 Warning

Recursive After Update

After-save Flows that update the same record can trigger recursion, causing unintended behavior or performance issues. Avoid updating the triggering record in after-save Flows; use before-save Flows instead to prevent recursion.

Rule ID: recursive-record-update Class Name: RecursiveAfterUpdate Severity: 🟡 Warning


Suggestions

These rules highlight areas where Flows can be improved. Following them increases reliability and long-term maintainability.

Action Call In A Loop

Repeatedly invoking Apex actions inside a loop can exhaust governor limits and lead to performance issues. Where possible, bulkify your logic by moving the action call outside the loop and passing a collection variable instead.

Rule ID: action-call-in-loop Class Name: ActionCallsInLoop Severity: 🟡 Warning

Get Record All Fields

Avoid using Get Records to retrieve all fields unless necessary. This improves performance, reduces processing time, and limits exposure of unnecessary data.

Rule ID: get-record-all-fields Class Name: GetRecordAllFields Severity: 🟡 Warning

Inactive Flow

Inactive Flows should be deleted or archived to reduce risk. Even when inactive, they can cause unintended record changes during testing or be activated as subflows. Keeping only active, relevant Flows improves safety and maintainability.

Rule ID: inactive-flow Class Name: InactiveFlow Severity: 🟡 Warning

Invalid API Version Auto-Fix

Flows running on outdated API versions may behave inconsistently when newer platform features or components are used. From API version 50.0 onward, the API Version attribute explicitly controls Flow runtime behavior. Keeping Flows aligned with a supported API version helps prevent compatibility issues and ensures predictable execution.

Rule ID: invalid-api-version Class Name: APIVersion Severity: 🟡 Warning

| Option | Type | Default | Description | |--------|------|---------|-------------| | expression | expression | >= 50 | Comparison expression for API version (e.g., >= 58, < 50, === 60) |

Missing Filter Record Trigger Beta

Record-triggered Flows without filters on changed fields or entry conditions execute on every record change. Adding filters ensures the Flow runs only when needed, improving performance.

Rule ID: missing-record-trigger-filter Class Name: MissingFilterRecordTrigger Severity: 🟡 Warning

Same Record Field Updates

Before-save Flows can safely update the triggering record directly via $Record, applying changes efficiently without extra DML operations. Using before-save updates improves performance

Rule ID: same-record-field-updates Class Name: SameRecordFieldUpdates Severity: 🟡 Warning

Cognitive Complexity Beta

Flows with deeply nested loops and decisions are hard to understand. Unlike cyclomatic complexity which counts paths, cognitive complexity penalizes nesting depth. Consider extracting nested logic into subflows.

Rule ID: cognitive-complexity Class Name: CognitiveComplexity Severity: 🔵 Note

| Option | Type | Default | Description | |--------|------|---------|-------------| | threshold | number | 15 | Maximum cognitive complexity score before triggering a violation |

Excessive Cyclomatic Complexity

High numbers of loops and decision elements increase a Flow's cyclomatic complexity. To maintain simplicity and readability, consider using subflows or splitting a Flow into smaller, ordered Flows.

Rule ID: excessive-cyclomatic-complexity Class Name: CyclomaticComplexity Severity: 🔵 Note

| Option | Type | Default | Description | |--------|------|---------|-------------| | threshold | number | 25 | Maximum cyclomatic complexity score before triggering a violation |

Missing Trigger Order

Record-triggered Flows without a specified Trigger Order may execute in an unpredictable sequence. Setting a Trigger Order ensures your Flows run in the intended order.

Rule ID: unspecified-trigger-order Class Name: TriggerOrder Severity: 🔵 Note

Record ID as String Beta

Flows that use a String variable for a record ID instead of receiving the full record introduce unnecessary complexity and additional Get Records queries. Using the complete record simplifies the Flow and improves performance.

Rule ID: record-id-as-string Class Name: RecordIdAsString Severity: 🔵 Note

Transform Instead of Loop Beta

Loop elements that perform direct Assignments on each item can slow down Flows. Using Transform elements allows bulk operations on collections, improving performance and reducing complexity.

Rule ID: transform-instead-of-loop Class Name: TransformInsteadOfLoop Severity: 🔵 Note


Layout

Focused on naming, documentation, and organization, these rules ensure Flows remain clear, easy to understand, and maintainable as automations grow.

Flow Naming Convention

Using clear and consistent Flow names improves readability, discoverability, and maintainability. A good naming convention helps team members quickly understand a Flow's purpose—for example, including a domain and brief description like Service_OrderFulfillment. Adopt a naming pattern that aligns with your organization's standards.

Rule ID: invalid-naming-convention Class Name: FlowName Severity: 🔴 Error

| Option | Type | Default | Description | |--------|------|---------|-------------| | expression | expression | [A-Za-z0-9]+_[A-Za-z0-9]+ | Regex pattern for valid Flow names |

Missing Flow Description

Flow descriptions are essential for documentation and maintainability. Include a description for each Flow, explaining its purpose and where it's used.

Rule ID: missing-flow-description Class Name: FlowDescription Severity: 🔴 Error

Missing Metadata Description Beta

Elements and metadata without a description reduce clarity and maintainability. Adding descriptions improves readability and makes your automation easier to understand.

Rule ID: missing-metadata-description Class Name: MissingMetadataDescription Severity: 🟡 Warning

Unclear API Name

Elements with unclear or duplicated API names, like Copy_X_Of_Element, reduce Flow readability. Make sure to update the API name when copying elements to keep your Flow organized.

Rule ID: unclear-api-naming Class Name: CopyAPIName Severity: 🟡 Warning

Unreachable Element Auto-Fix

Unconnected elements never execute and add unnecessary clutter. Remove or connect unused Flow elements to keep Flows clean and efficient.

Rule ID: unreachable-element Class Name: UnconnectedElement Severity: 🟡 Warning

Unused Variable Auto-Fix

Unused variables are never referenced and add unnecessary clutter. Remove them to keep Flows efficient and easy to maintain.

Rule ID: unused-variable Class Name: UnusedVariable Severity: 🟡 Warning

Missing Auto Layout Auto-Fix

Auto-Layout automatically arranges and aligns Flow elements, keeping the canvas organized and easier to maintain. Enabling it saves time and improves readability.

Rule ID: missing-auto-layout Class Name: AutoLayout Severity: 🔵 Note

System (subcategory)

System rules are a subset of Layout rules that detect structural issues normally prevented by the Flow Builder UI. See System Rules Documentation for the full list.

Configuration

It is recommend to configure and define:

  • The severity of violating any specific rule.
  • Expressions used for rules, such as REGEX patterns and comparison operators.
  • Any known exceptions that should be ignored during scanning.
  • (Optional) Implement filters based on a severity threshold or rule categories.

Most distributions automatically load configuration from:

  • .flow-scanner.yml
  • .flow-scanner.json
  • package.json"flowScanner" key
{
  "rules": {
    // rule customizations (severity, expression, enabled, ...)
  },
  "exceptions": {
    // flow → rule → result suppressions
  },
  "threshold": "error",                    // only consider errors
  "categories": ["problem", "layout"]  // only run rules from these categories
}

Configure Rules

By default, all default rules are executed. You can customize individual rules and override the rules to be executed without having to specify every rule. Below is a breakdown of the available attributes of rule configuration:

{
  "rules": {
    "<RuleId>": {
      "severity": "<Severity>", // Override severity level
      "expression": "<Expression>", // Override rule expression
      "message": "<Message>", // Set custom message
      "messageUrl": "<URL>", // Set custom documentation URL
      "enabled": false, // Disable this rule
    }
  }
}

Configure Severity

Available values for severity are error, warning and note. If no severity is provided, a default value is applied. Configure the severity per rule as demonstrated below:

{
  "rules": {
    "record-id-as-string": {
      "severity": "warning",
    },
    "unclear-api-naming": {
      "severity": "error",
    }
  }
}

Customize Rules

Some rules are configurable and allow overriding their default expressions, or setting a threshold as shown in the examples below.

{
  "rules": {
    "invalid-api-version": {
      "expression": "===58" // comparison expression
    },
    "invalid-naming-convention": {
      "expression": "[A-Za-z0-9]" // regular expression
    },
    "excessive-cyclomatic-complexity": {
      "threshold": 10 // threshold
    }
  }
}

Customize Rule Messages

If not provided, message shows the standard rule summary and messageUrl links to the README; providing either overrides the default behavior.

{
  "rules": {
    "dml-in-loop": {
      "message": "Avoid DML inside loops. Bulkify operations instead.",
      "messageUrl": "https://internal.docs.company.com/salesforce/flow-dml-best-practices"
    }
  }
}

Disable Rules

To disable a rule, set "enabled": false as shown below:

{
  "rules": {
    "dml-in-loop": {
      "enabled": false
    }
  }
}

Define Exceptions

Defining exceptions allows you to exclude specific scenarios from rule enforcement. Exceptions can be specified at the flow, rule, or result level to provide fine-grained control. Below is a breakdown of the available attributes of exception configuration:

{
  "exceptions": {
    "<FlowName>": {
      "<RuleId>": [
        "<ResultName>", // Suppress a result
        "*", // Wildcard to suppress all results
        ...
      ]
    },
    ...
  }
}

Example

{
  "exceptions": {
    "MyFlow": {
      "hardcoded-id": ["Old_Lookup_1"],
      "missing-null-handler": ["*"]
    }
  }
}

Exclude Flows

Exclude by File Path (Node.js only)

Use glob patterns to exclude flows based on their file system location. This is useful for excluding entire directories or specific name patterns:

{
  "ignore": [
    "**/testing/**",
    "**/*_Deprecated.flow-meta.xml"
  ]
}

Environment compatibility: requires Node.js(file system access) and is not available when using the Core Library in browser/web environments.

Exclude by Flow API Name (Browser-compatible)

Exclude specific flows by their unique API names, regardless of their location. This is particularly useful for:

  • Excluding specific flows without knowing their exact file path
  • Working with metadata API deployments where directory structures may vary
  • More precise control than path-based patterns
{
  "ignoreFlows": [
    "My_Legacy_Flow",
    "Temporary_Test_Flow",
    "Deprecated_Process_Builder"
  ]
}

Environment compatibility: works in all environments including Node.js and browser/web distributions, as it operates on parsed flow data rather than file system paths.

Scan Options

Severity Threshold

Only report on violations at or above a chosen severity level:

{ "threshold": "error" }

Filter by category

Restrict the scan to specific categories of rules:

{ "categories": ["problem", "layout"] }

Beta Mode

New rules are introduced in Beta mode before being added to the default ruleset. To include current Beta rules, enable the optional betamode parameter in your configuration:

{ "betaMode": true }

Rule Mode

By default, Lightning Flow Scanner runs all default rules and merges any custom configurations you provide. If instead, you want to run only the rules you explicitly specify, use:

{ "ruleMode": "isolated" }

Installation

GitHub stars GitHub contributors License npm

sf plugins install lightning-flow-scanner

OR

npm install -g lightning-flow-scanner

Development

This project optionally uses Volta to guarantee the exact same Node.js and tool versions for every contributor. Install Volta with:

MacOs/Linux:

curl https://get.volta.sh | bash

Windows:

winget install Volta.Volta 

Volta will automatically install and lock the tool versions defined in package.json.

  1. Clone the repository

    git clone https://github.com/Flow-Scanner/lightning-flow-scanner.git
  2. Install dependencies:

    pnpm install
  3. Compile:

    pnpm run build:cli
  4. Run tests:

    pnpm test:cli
  5. Linking the Salesforce CLI module locally(Optional):

    To link the module, run:

    sf plugins link packages/cli