Skip to content

Execution Paths

The path generator enumerates every distinct execution path through your Effect program. Each path represents a unique sequence of steps that could execute given certain conditions - which branches are taken, which errors occur, and how loops iterate.

The mermaid-paths format renders each execution path as a separate diagram:

Terminal window
npx effect-analyze ./src/transfer.ts --format mermaid-paths
import { analyze, generatePaths } from "effect-analyzer/analysis"
import { Effect } from "effect"
const ir = await Effect.runPromise(analyze("./src/transfer.ts").single)
const paths = generatePaths(ir)
for (const path of paths) {
console.log(`Path: ${path.steps.map(s => s.name ?? s.nodeId).join("")}`)
console.log(` Conditions: ${path.conditions.length}`)
console.log(` Has loops: ${path.hasLoops}`)
}

Each EffectPath contains:

Field Type Description
steps PathStepRef[] Ordered sequence of steps in this path
conditions PathCondition[] Conditions that must hold for this path to execute
hasLoops boolean Whether this path passes through a loop

Each PathStepRef includes:

Field Type Description
nodeId string Reference to the IR node
name string | undefined Human-readable step name
repeated boolean Whether this step repeats (loop body)

Control path generation with PathGeneratorOptions:

const paths = generatePaths(ir, {
maxPaths: 100, // Cap at 100 paths (default: 1000)
expandLoops: true, // Expand loop bodies into separate paths
maxLoopIterations: 3 // Maximum loop unrolling depth (default: 3)
})
Option Type Default Description
maxPaths number 1000 Maximum paths to generate before stopping
expandLoops boolean false Expand loop iterations as separate paths
maxLoopIterations number 3 How many iterations to unroll when expandLoops is true

Use generatePathsWithMetadata for additional information:

import { generatePathsWithMetadata } from "effect-analyzer/analysis"
const result = generatePathsWithMetadata(ir, { maxPaths: 50 })
console.log(result.paths.length) // Number of paths generated
console.log(result.limitHit) // true if maxPaths was reached

Use calculatePathStatistics to get aggregate statistics across all paths:

import { generatePaths, calculatePathStatistics } from "effect-analyzer/analysis"
const paths = generatePaths(ir)
const stats = calculatePathStatistics(paths)
console.log(stats.totalPaths) // Total number of paths
console.log(stats.avgPathLength) // Average steps per path
console.log(stats.maxPathLength) // Longest path
console.log(stats.pathsWithLoops) // How many paths include loops
console.log(stats.uniqueConditions) // Distinct branching conditions

Use filterPaths to narrow down to specific paths of interest:

import { generatePaths, filterPaths } from "effect-analyzer/analysis"
const paths = generatePaths(ir)
// Filter to paths that include a specific step
const withDebit = filterPaths(paths, {
includeStep: "debit",
})
// Filter to paths with error conditions
const errorPaths = filterPaths(paths, {
hasErrors: true,
})

Paths are the input to the Test Coverage Matrix. Each path becomes a test case describing what conditions to set up and what steps to verify.

import { generatePaths, generateTestMatrix } from "effect-analyzer/analysis"
const paths = generatePaths(ir)
const matrix = generateTestMatrix(paths)