Node.js bindings

The @fallow-cli/fallow-node package exposes the main fallow analyses as async Node.js functions. The bindings avoid CLI subprocesses and JSON parsing, reuse the shared analysis layer, and return the same JSON report envelopes the CLI emits for --format json. The optional detectSimilarCode function verifies and launches its exact local model companion.

Use them when you want to embed fallow inside another tool, a Node.js server, an editor extension, or a custom script that otherwise would shell out to the CLI.

Install

npm install @fallow-cli/fallow-node

The package is a native NAPI-RS addon with platform-specific optionalDependencies for macOS (arm64, x64), Linux (gnu + musl on arm64 and x64), and Windows (x64, arm64). npm picks the matching binary at install time.

Node 18 or newer is required.

Basic usage

import {
  detectDeadCode,
  detectDuplication,
  detectSimilarCode,
  computeHealth,
} from '@fallow-cli/fallow-node';

const deadCode = await detectDeadCode({
  root: process.cwd(),
  explain: true,
});

console.log(deadCode.summary.total_issues);
console.log(deadCode.unused_exports);

const similar = await detectSimilarCode({
  root: process.cwd(),
  threshold: 0.8,
  top: 20,
});

console.log(similar.candidates);

Every function returns a promise resolving to the same JSON envelope the CLI emits when called with --format json: a top-level kind discriminator on typed roots, schema_version, summary, relative paths, injected actions arrays, and (when explain: true) a _meta block with docs links.

API

All eight functions are async and accept an optional options object. Unknown fields are ignored. Enum-like fields take lowercase CLI-style literals ("mild", "cyclomatic", "handle", "low").

FunctionCLI equivalentReturns
detectDeadCode(options?)fallow dead-code --format jsonDeadCodeReport
detectCircularDependencies(options?)fallow dead-code --circular-depsDeadCodeReport filtered to the circular_dependencies category
detectBoundaryViolations(options?)fallow dead-code --boundary-violationsDeadCodeReport filtered to the boundary_violations category
detectDuplication(options?)fallow dupes --format jsonDuplicationReport
detectSimilarCode(options?)fallow similar-code --format jsonSimilarCodeReport with unverified candidates
detectFeatureFlags(options?)fallow flags --format jsonFeatureFlagsReport
computeComplexity(options?)fallow health --format jsonHealthReport
computeHealth(options?)fallow health --format jsonAlias for computeComplexity with a product-oriented name

Shared options

Every options object extends AnalysisOptions:

FieldTypeDefaultPurpose
rootstringprocess.cwd()Project root directory
configPathstringauto-discoverPath to .fallowrc.json / .fallowrc.jsonc / fallow.toml / .fallow.toml
noCachebooleanfalseSkip the extraction cache
threadsnumberCPU countParallelism; must be > 0
productionbooleanfalseProduction mode (excludes test/build files)
changedSincestringunsetScope analysis to files changed since a git ref
workspacestring[]unsetRestrict to named workspace packages (monorepo)
changedWorkspacesstringunsetRestrict to workspaces whose files changed since a git ref. Mutually exclusive with workspace.
explainbooleanfalseInject a _meta block with metric / rule documentation

Dead-code options

detectDeadCode, detectCircularDependencies, and detectBoundaryViolations accept the same superset, DeadCodeOptions. Setting any of the filter booleans below restricts the report to the selected issue types; leaving them all unset returns every category (the CLI's default behavior).

FieldType
unusedFiles / unusedExports / unusedDeps / unusedTypesboolean
unusedEnumMembers / unusedClassMembersboolean
unresolvedImports / unlistedDepsboolean
duplicateExports / circularDeps / boundaryViolations / staleSuppressionsboolean
filesstring[]
includeEntryExportsboolean

Duplication options

DuplicationOptions adds duplication-specific fields on top of AnalysisOptions:

FieldTypeDefault
mode"strict" | "mild" | "weak" | "semantic""mild"
nearbooleanfalse
minTokensnumber50
minLinesnumber5
minOccurrencesnumber2
thresholdnumber0 (no limit)
skipLocalbooleanfalse
crossLanguagebooleanfalse
ignoreImportsbooleantrue
topnumberunset

Each returned clone group includes spread. Near-miss groups also include similarity. Duplication stats can include clone_groups_ignored and near_candidates_skipped; zero values are omitted.

Similar-code options

SimilarCodeOptions adds model-backed discovery fields on top of AnalysisOptions:

FieldTypeDefault
thresholdnumber from 0 to 1config or 0.8
minLinespositive numberconfig or 3
toppositive numberunset
filesstring[]unset

The loader resolves the exact-version fallow-similar-code package and verifies its native binary before source is read. detectSimilarCode never downloads the model. After explicit user approval, run fallow similar-code setup --local once through the CLI. Results remain unverified. Read Semantic similar code before using a score to plan a refactor.

Feature flag options

FeatureFlagsOptions adds one field on top of AnalysisOptions:

FieldTypeDefault
topnumberunset

Complexity / health options

ComplexityOptions mirrors the fallow health flag surface:

FieldTypeNotes
maxCyclomatic, maxCognitivenumber (0 to 65535)Per-function thresholds
maxCrapnumberCRAP score ceiling
topnumberTruncate rankings
sort"cyclomatic" | "cognitive" | "lines" | "severity"Finding sort order (default cyclomatic)
complexity, fileScores, coverageGaps, hotspots, targets, scorebooleanEnable individual report sections
ownership, ownershipEmailsboolean + "raw" | "handle" | "anonymized" | "hash"Ownership-aware hotspots
effort"low" | "medium" | "high"Filter refactoring targets
since, minCommitsstring, numberGit history window for hotspots
coverage, coverageRootstringIstanbul coverage file + root

Structured errors

Rejected promises throw a FallowNodeError (a plain Error with extra fields) that mirrors the CLI's structured error surface:

import type { FallowNodeError } from '@fallow-cli/fallow-node';

try {
  await detectDeadCode({ root: '/does/not/exist' });
} catch (err) {
  const e = err as FallowNodeError;
  console.error(e.name);      // "FallowNodeError"
  console.error(e.message);   // "analysis root does not exist: ..."
  console.error(e.exitCode);  // 2
  console.error(e.code);      // "FALLOW_INVALID_ROOT"
  console.error(e.context);   // "analysis.root"
  console.error(e.help);      // optional remediation hint
}

The exit-code ladder matches the CLI: 0 ok, 2 generic / validation, 7 network (paid features only), etc.

When to use bindings vs the CLI

Use the bindingsUse the CLI
Embedding fallow in a long-running Node process (editor, devserver, CI runner)One-off invocation from a shell script or GitHub Action
You already have a Node runtime and want to skip subprocess + JSON parse overheadYou want zero runtime dependencies (native binary via npx fallow)
You need to call many analyses in sequence without re-initializingYou want CLI human output, progress bars, or interactive flags
You need typed access to the result envelope in TypeScriptYou consume the analysis from Rust, Python, Go, or any non-Node language

Both paths run the same analysis engine and return the same data. The bindings are not a reduced subset.

Limitations

  • The bindings wrap the one-shot analyses. Commands that write to the filesystem (fix, init, hooks install, hooks uninstall, audit --save-baseline, license activate, coverage setup) are not exposed.
  • detectSimilarCode is read-only and never performs its first model download. Use the CLI after explicit approval: fallow similar-code setup --local.
  • No baseline or regression support yet. Use the CLI's --baseline / --save-baseline flags for those workflows.
  • No watch mode. If you want incremental analysis, re-invoke the relevant function; fallow's per-file cache makes repeat runs fast.
  • The API surface is young; breaking changes between minor versions are possible until it stabilizes.