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"
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:

FieldTypeDescription
stepsPathStepRef[]Ordered sequence of steps in this path
conditionsPathCondition[]Conditions that must hold for this path to execute
hasLoopsbooleanWhether this path passes through a loop

Each PathStepRef includes:

FieldTypeDescription
nodeIdstringReference to the IR node
namestring | undefinedHuman-readable step name
repeatedbooleanWhether 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)
})
OptionTypeDefaultDescription
maxPathsnumber1000Maximum paths to generate before stopping
expandLoopsbooleanfalseExpand loop iterations as separate paths
maxLoopIterationsnumber3How many iterations to unroll when expandLoops is true

Use generatePathsWithMetadata for additional information:

import { generatePathsWithMetadata } from "effect-analyzer"
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"
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"
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"
const paths = generatePaths(ir)
const matrix = generateTestMatrix(paths)