| name | effect-analyzer |
| description | Use when working on the effect-analyzer package - static analysis for Effect-TS code. Covers architecture, IR types, adding analyzers, output renderers, CLI flags, diff/regression detection, interactive HTML, test matrix, coverage audit, testing with fixtures, and the fluent analyze() API. Triggers on changes to packages/effect-analyzer or questions about Effect program analysis, mermaid diagrams, or IR nodes. |
effect-analyzer
Static analysis tool for Effect-TS programs. Parses TypeScript via ts-morph, builds an IR (Intermediate Representation), then renders diagrams, metrics, and reports without executing code.
Architecture
CLI (cli.ts)
→ analyze.ts (fluent API: analyze(path).single() | .all() | .named())
→ static-analyzer.ts (analyzeEffectFile / analyzeEffectSource)
→ program-discovery.ts (find Effect programs in AST)
→ core-analysis.ts (build IR tree from AST nodes)
→ effect-analysis.ts (pipes, calls, generators)
→ alias-resolution.ts (resolve Effect aliases/re-exports)
→ type-extractor.ts (extract Effect/Stream/Layer type signatures)
→ output/*.ts (render IR → mermaid, json, html, explain, etc.)
→ diff/*.ts (semantic diff between program versions)
Key data flow: Source file → ts-morph AST → StaticEffectIR (IR) → output format
Core Types
IR root:
StaticEffectIR { root: StaticEffectProgram, metadata, serviceDefinitions, warnings }
Programs contain children — a tree of StaticFlowNode (30+ types):
effect — individual Effect calls (with callee, semanticRole, serviceCall)
generator / pipe — Effect.gen blocks and pipe chains
parallel / race — concurrency patterns
error-handler — catch/catchAll/catchTag (37 handler variants)
retry / timeout / resource — resilience patterns
conditional / decision / switch — control flow
layer / stream / fiber — Effect ecosystem constructs
transform — map/flatMap/tap operations
opaque / unknown — unsupported or unanalyzable
- Plus:
terminal, try-catch, loop, cause, exit, schedule, match, scope-resource, and more
All types use readonly everywhere. See StaticFlowNode union in src/types.ts for the full set.
CLI Usage
effect-analyze [PATH] [options]
PATH defaults to . (current directory). When PATH is a directory, analyzes all TypeScript files and writes colocated .effect-analysis.md next to each file containing Effect programs.
Output Formats
effect-analyze <path> --format <fmt>:
| Format | Description |
|---|
auto | Best diagram for program (default) |
json | Raw IR as JSON |
mermaid | Generic flowchart |
mermaid-paths | Path-based rendering (with style-guide heuristics) |
mermaid-enhanced | Enhanced Mermaid with colors/styles |
mermaid-railway | Happy path + error branches |
mermaid-services | Service dependency map |
mermaid-errors | Error propagation |
mermaid-decisions | Control flow |
mermaid-causes | Cause/error cause chain diagram |
mermaid-concurrency | Parallel/race |
mermaid-timeline | Step sequence |
mermaid-layers | Layer composition |
mermaid-retry | Retry/resilience patterns |
mermaid-testability | Testing readiness diagram |
mermaid-dataflow | Variable flow |
mermaid-statechart | State machine as stateDiagram-v2 (coverage-annotated) |
svg-statechart | Self-contained XState-styled statechart SVG (coverage-annotated) |
statechart-html | Local visualizer page: SVG, coverage, XState export |
xstate-config | createMachine() config for stately.ai/viz |
statechart-coverage | Completeness report; exits non-zero on warnings (CI gate) |
explain | Plain-English narrative |
summary | One-liner |
stats | Complexity metrics |
matrix | Dependency matrix |
showcase | Multi-program showcase |
api-docs | API documentation (HttpApi) |
openapi-paths | OpenAPI paths JSON |
openapi-runtime | Runtime OpenAPI via OpenApi.fromApi() |
migration | Migration opportunities |
Diff & Regression Detection
Compare program versions structurally (not text diff):
effect-analyze --diff v1.ts v2.ts
effect-analyze --diff src/wf.ts
effect-analyze --diff main:src/wf.ts src/wf.ts
effect-analyze --diff gh:#123
effect-analyze --diff gh:#123 src/wf.ts
effect-analyze --diff v1.ts v2.ts --regression
effect-analyze --diff v1.ts v2.ts --include-trivial
Renderers: renderDiffMarkdown(), renderDiffJSON(), renderDiffMermaid()
Interactive HTML
effect-analyze ./program.ts --format mermaid --html
effect-analyze ./program.ts --html --html-output=docs/program.html
Self-contained HTML with 6 themes: midnight | ocean | ember | forest | daylight | paper. Features: search/filter, click nodes for source location/IR, path explorer, complexity heatmap, data flow and error flow overlay toggles.
Library: renderInteractiveHTML(ir, options) from src/output/html.ts
Colocated Analysis Docs
effect-analyze src/
effect-analyze src/ --no-colocate
effect-analyze src/ --no-colocate-enhanced
effect-analyze src/ --colocate-suffix=analysis
effect-analyze ./file.ts --colocate
Coverage Audit
effect-analyze ./src --coverage-audit
effect-analyze ./src --coverage-audit --json-summary
effect-analyze ./src --coverage-audit --show-suspicious-zeros
effect-analyze ./src --coverage-audit --show-by-folder
effect-analyze ./src --coverage-audit --per-file-timing
effect-analyze ./src --coverage-audit --min-meaningful-nodes 3
effect-analyze ./src --coverage-audit --exclude-from-suspicious-zero "test"
effect-analyze ./src --coverage-audit --known-effect-internals-root ./packages/core
Reports: discovered/analyzed/failed counts + percentages, suspicious zeros, top unknown files/reasons, folder aggregation.
Diagram Quality
effect-analyze ./src --quality
effect-analyze ./src --quality --quality-eslint .cache/eslint.json
Test Matrix Generation
Library API for generating test coverage matrices from program paths:
import { generateTestMatrix, formatTestMatrixMarkdown, formatTestMatrixAsCode, formatTestChecklist } from 'effect-analyzer';
Watch Mode
effect-analyze ./program.ts --watch
effect-analyze ./program.ts -w --cache
All CLI Flags
| Flag | Description |
|---|
-f, --format <fmt> | Output format (see table above) |
--export <name> | For openapi-runtime: HttpApi export name |
-o, --output <file> | Output file (default: stdout) |
-d, --direction <dir> | Mermaid direction: TB, LR, BT, RL |
-c, --compact | Compact output |
--pretty | Pretty-print (default) |
--tsconfig <path> | Custom tsconfig.json path |
--no-metadata | Exclude metadata |
--colocate | Single-file: write colocated .md |
--no-colocate | Project mode: skip writing files |
--no-colocate-enhanced | Standard Mermaid in colocated docs |
--colocate-suffix <s> | Custom suffix (default: effect-analysis) |
-q, --quiet | Minimal output |
--no-color | Disable colors |
-w, --watch | Watch mode |
--cache | Cache for watch |
-m, --migration | Migration assistant |
--diff | Semantic diff mode |
--regression | Flag regressions in diff |
--include-trivial | Include trivial diff changes |
--coverage-audit | Project coverage audit |
--json-summary | Audit: JSON-only output (CI) |
--show-suspicious-zeros | Audit: list 0-program Effect files |
--show-top-unknown | Audit: top files by unknown rate |
--show-top-unknown-reasons | Audit: top unknown reasons |
--show-by-folder | Audit: folder aggregation |
--per-file-timing | Audit: per-file durations |
--min-meaningful-nodes <n> | Filter small programs |
--exclude-from-suspicious-zero <pat> | Exclude pattern (repeatable) |
--known-effect-internals-root <path> | Treat local imports as Effect |
--quality | Diagram quality heuristics |
--quality-eslint <path> | ESLint JSON for quality hints |
--style-guide / --no-style-guide | Style guide heuristics |
--service-map / --no-service-map | Service map (default: on) |
--html | Interactive HTML output |
--html-output <path> | HTML output path |
Library API
Primary Analysis
import { analyze } from 'effect-analyzer';
const ir = await Effect.runPromise(analyze('./program.ts').single());
const all = await Effect.runPromise(analyze('./program.ts').all());
const named = await Effect.runPromise(analyze('./program.ts').named('myProgram'));
Advanced Analyses
All exported from src/index.ts:
- Composition:
analyzeProgramGraph(), analyzeProjectComposition()
- Data flow:
buildDataFlowGraph(), buildLayerDependencyGraph()
- Error flow:
analyzeErrorFlow(), analyzeErrorPropagation()
- Service flow:
analyzeServiceFlow(), buildProjectServiceMap()
- State flow:
analyzeStateFlow() — Ref/state tracking
- Scope/resource:
analyzeScopeResource() — Resource lifecycle
- Observability:
analyzeObservability() — Spans, logs, metrics
- DI completeness:
checkDICompleteness() — Service satisfaction
- Strict diagnostics:
validateStrict() — Strict mode validation
- Match analysis:
analyzeMatch() — Match statement patterns
- State machines:
analyzeStateMachines(filePath) — extracts FSMs (transition tables + Match.when functions + nested Match.tags state/event dispatch), declared alphabet from types/Schema/Schema.TaggedClass/Schema.TaggedRequest; computeStateMachineCoverage() flags unhandled events / unreachable states / undeclared symbols. Renderers: renderStatechartMermaid, renderStatechartSVG, renderStatechartVisualizerHTML, renderXStateConfig, renderCoverageReport, hasCoverageWarnings. CLI handled by runStatechartMode (early dispatch, not the IR pipeline).
- Convention guide:
packages/effect-analyzer/state-machine-conventions.md
- Platform:
analyzePlatformUsage() — Platform detection
- Testing:
analyzeTestingPatterns() — Test pattern detection
- Version:
getEffectVersion(), checkVersionCompat()
- Generator:
analyzeGenYields() — Yield analysis
- SQL:
analyzeSqlPatterns() — SQL pattern detection
- RPC:
analyzeRpcPatterns() — RPC pattern analysis
- Batching:
analyzeRequestBatching() — Request batching
- STM:
analyzeStm() — Software transactional memory
- Playground:
exportForPlayground(), encodePlaygroundPayload()
Output Renderers
import {
renderStaticMermaid, renderEnhancedMermaid, renderRailwayMermaid,
renderInteractiveHTML,
renderDiffMarkdown, renderDiffJSON, renderDiffMermaid,
generateTestMatrix, formatTestMatrixMarkdown, formatTestMatrixAsCode, formatTestChecklist,
renderDocumentation, renderMultiProgramDocs,
generateShowcase,
computeProgramDiagramQuality, buildTopOffendersReport,
} from 'effect-analyzer';
Quick Reference
Adding a New Analyzer
- Create
src/my-analysis.ts with a function taking StaticEffectIR
- Export from
src/index.ts
- Walk IR tree recursively:
import { getStaticChildren } from './analysis-utils';
function visit(node: StaticFlowNode) {
for (const child of getStaticChildren(node)) visit(child);
}
visit(ir.root);
- Add tests with fixtures in
src/__fixtures__/
Adding a New Output Format
- Create
src/output/my-format.ts with render function taking StaticEffectIR
- Export from
src/index.ts
- Add CLI flag in
src/cli.ts under the --format option
- Add to
src/output/auto-diagram.ts if it should be auto-selected
Adding a New Node Type
- Add type to
StaticFlowNode union in src/types.ts
- Handle in
core-analysis.ts or effect-analysis.ts
- Update
getStaticChildren() in analysis-utils.ts
- Handle in relevant output renderers (
output/mermaid.ts, etc.)
Testing
Framework: Vitest. Tests colocated as *.test.ts.
Pattern: Fixture-based analysis validation:
import { analyze } from './analyze';
import { Effect } from 'effect';
it('detects parallel patterns', { timeout: 20_000 }, async () => {
const irs = await Effect.runPromise(analyze(fixturePath).all());
expect(irs.length).toBeGreaterThanOrEqual(1);
});
Fixtures: src/__fixtures__/*.ts — add new patterns here, then reference in tests.
For source-level tests (no fixture file):
import { analyzeEffectSource } from './static-analyzer';
const [ir] = await Effect.runPromise(analyzeEffectSource(`
import { Effect } from "effect";
export const myProgram = Effect.gen(function* () { ... });
`));
Run: pnpm test (all), pnpm test:watch (dev), pnpm quality (lint + type-check + test)
Build
pnpm build
pnpm type-check
pnpm lint
pnpm quality
Entries: src/index.ts (library), src/cli.ts (CLI), src/lsp/server.ts (LSP)
Key Patterns
- Effect-first: All async operations use
Effect.gen, Effect.try, Effect.forEach
- Visitor pattern: Recursive walks via
getStaticChildren(node)
- WeakMap caching:
effectAliasCache, nodeTextCache keyed by SourceFile/Node
- Pattern maps:
ERROR_HANDLER_PATTERNS, CONDITIONAL_PATTERNS, TRANSFORM_OPS in analysis-patterns.ts
- Typed errors:
AnalysisError with codes (NO_EFFECTS_FOUND, FILE_NOT_FOUND)
- Semantic roles: Nodes tagged with
SemanticRole for filtering/styling
Common Mistakes
- Forgetting
readonly on new type fields — all IR types are immutable
- Not handling new node types in
getStaticChildren — causes silent omission in traversals
- Long timeouts in tests — ts-morph project creation is slow; use
{ timeout: 20_000 }
- Mutating IR — create new objects, never mutate existing nodes