Node.js bindings
Run fallow analyses from your own Node.js code and get typed results. The NAPI bindings need no CLI subprocess or JSON parsing and return the same JSON envelopes as the CLI.
With @fallow-cli/fallow-node, you call the main fallow analyses as async Node.js functions. You do not start a CLI subprocess or parse JSON. The functions use the same analysis layer as the CLI and return the same JSON report envelopes as --format json. The optional detectSimilarCode function verifies and starts its local model companion, which must be the exact matching version.
Use the bindings to embed fallow in code that would otherwise call the CLI:
- another tool
- a Node.js server
- an editor extension
- a custom script
Install
npm install @fallow-cli/fallow-node
The package is a native NAPI-RS addon. It has platform-specific optionalDependencies for these platforms:
- macOS (arm64, x64)
- Linux (gnu and musl on arm64 and x64)
- Windows (x64, arm64)
npm installs the matching binary.
The package requires Node 22 or newer.
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);
Each function returns a promise. The promise resolves to the same JSON envelope that the CLI prints with --format json. The envelope has:
- a top-level
kinddiscriminator on typed roots schema_versionsummary- relative paths
- injected
actionsarrays - a
_metablock with docs links, whenexplain: true
API
All nine functions are async and take an optional options object. The functions ignore unknown fields. Enum-like fields take lowercase CLI-style literals, such as "mild", "cyclomatic", "handle", and "low".
| Function | CLI equivalent | Returns |
|---|---|---|
detectDeadCode(options?) | fallow dead-code --format json | DeadCodeReport |
detectCircularDependencies(options?) | fallow dead-code --circular-deps | DeadCodeReport filtered to the circular_dependencies category |
detectBoundaryViolations(options?) | fallow dead-code --boundary-violations | DeadCodeReport filtered to the boundary_violations category |
detectArchitecture(options?) | fallow architecture --format json | ArchitectureReport: the DeadCodeReport body with kind: "architecture" and its own schema_version |
detectDuplication(options?) | fallow dupes --format json | DuplicationReport |
detectSimilarCode(options?) | fallow similar-code --format json | SimilarCodeReport with unverified candidates |
detectFeatureFlags(options?) | fallow flags --format json | FeatureFlagsReport |
computeComplexity(options?) | fallow health --format json | HealthReport |
computeHealth(options?) | fallow health --format json | Alias for computeComplexity |
The --circular-deps and --boundary-violations flags of fallow dead-code are deprecated aliases. They select only one issue type, so they stay the exact CLI equivalents here. fallow architecture reports all import cycles, boundary violations and policy violations together. detectArchitecture is the binding for that command. detectCircularDependencies and detectBoundaryViolations continue to return the dead-code report (kind: "dead-code").
Shared options
Every options object extends AnalysisOptions:
| Field | Type | Default | Purpose |
|---|---|---|---|
root | string | process.cwd() | Project root directory |
configPath | string | auto-discover | Path to .fallowrc.json / .fallowrc.jsonc / fallow.toml / .fallow.toml |
allowRemoteExtends | boolean | false | Let trusted config files extend HTTPS URLs (same as --allow-remote-extends) |
noCache | boolean | false | Skip the extraction cache |
threads | number | CPU count | Number of threads. Must be > 0 |
diffFile | string | unset | Path to a unified diff file for line-level scoping (same as --diff-file). Diffs from stdin work only in the CLI. |
production | boolean | false | Production mode (excludes test/build files) |
changedSince | string | unset | Scope analysis to files changed since a git ref |
workspace | string[] | unset | Restrict to the named workspace packages (monorepo) |
changedWorkspaces | string | unset | Restrict to workspaces with files changed since a git ref. Cannot be used with workspace. |
explain | boolean | false | Add a _meta block with metric and rule documentation |
Dead-code options
detectDeadCode, detectCircularDependencies, and detectBoundaryViolations take the same options type, DeadCodeOptions. When you set one or more filter booleans below, the report has only those issue types. When you set none, the report has every category, as the CLI does by default.
| Field | Type | Notes |
|---|---|---|
unusedFiles / unusedExports / unusedDeps / unusedTypes | boolean | |
privateTypeLeaks | boolean | Opt-in. Private type leaks show only when you set this filter. |
unusedEnumMembers / unusedClassMembers | boolean | |
unresolvedImports / unlistedDeps | boolean | |
duplicateExports / circularDeps / boundaryViolations / staleSuppressions | boolean | |
files | string[] | Restrict to specific files (like fallow dead-code path/to/file.ts) |
includeEntryExports | boolean | Report unused exports on entry files |
showCascade | boolean | Also report the unused exports, types, class members, and enum members of unused files. Default false: the report hides them and counts them in cascade_hidden. See Findings in unused files. |
findingIds | string[] | Report only the findings with these finding_id values, like fallow dead-code --finding-id. The report adds finding_id_query. See finding_id_query. Only detectDeadCode accepts it. detectCircularDependencies and detectBoundaryViolations reject it with the error code FALLOW_UNSUPPORTED_OPTION. |
typeAware | { enabled?, projects?, require? } | Type-aware TypeScript analysis: enabled (boolean), projects (string[] of tsconfig paths), and require ("best-effort" | "complete"). enabled: false keeps the call syntactic, also when the project config or FALLOW_TYPE_AWARE turns type-aware analysis on. When enabled is unset and one of these turns it on, the call rejects with the error code FALLOW_TYPE_AWARE_CONFIG_UNSUPPORTED. Set enabled to true or false to choose. |
Architecture options
detectArchitecture takes ArchitectureOptions. It adds these fields to AnalysisOptions:
| Field | Type | Notes |
|---|---|---|
cycles | boolean | Report only import cycles: circular dependencies, re-export cycles, and package cycles (like --cycles) |
boundaries | boolean | Report only boundary violations, boundary coverage, and forbidden calls (like --boundaries) |
policy | boolean | Report only rule-pack policy violations (like --policy) |
files | string[] | Report only the findings in these files |
noPackageBaselines | boolean | Ignore the per-package refs of workspaces.changedSince for this call |
When you set no selector, the report has all architecture issue types.
Duplication options
DuplicationOptions adds these fields to AnalysisOptions:
| Field | Type | Default |
|---|---|---|
mode | "strict" | "mild" | "weak" | "semantic" | "mild" |
near | boolean | false |
minTokens | number | 50 |
minLines | number | 5 |
minOccurrences | number | 2 |
threshold | number | 0 (no limit) |
skipLocal | boolean | false |
crossLanguage | boolean | false |
ignoreImports | boolean | true |
ignoreSymlinks | boolean | unset (the project config decides) |
top | number | unset |
Each returned clone group has spread. Near-miss groups also have similarity. ignoreSymlinks: true omits clone instances whose path is a symlink or lies under a symlinked directory. Otherwise, such an instance has is_symlink: true. Duplication stats can have clone_groups_ignored and near_candidates_skipped. The report leaves out these fields when the value is zero.
Similar-code options
SimilarCodeOptions adds these fields for model-based search to
AnalysisOptions:
| Field | Type | Default |
|---|---|---|
threshold | number from 0 to 1 | config or 0.8 |
minLines | positive number | config or 3 |
top | positive number | unset |
files | string[] | unset |
Before it reads any source, the loader finds the fallow-similar-code package
of the exact matching version and verifies its native binary.
detectSimilarCode never downloads the model. To get the model, run
fallow similar-code setup --local once through the CLI, after explicit user
approval. The results are unverified. Read
Semantic similar code before you use a score to plan
a refactor.
Feature flag options
FeatureFlagsOptions adds one field to AnalysisOptions:
| Field | Type | Default |
|---|---|---|
top | number | unset |
Complexity / health options
ComplexityOptions matches the fallow health flags:
| Field | Type | Notes |
|---|---|---|
maxCyclomatic, maxCognitive | number (0 to 65535) | Per-function thresholds |
maxCrap | number | CRAP score ceiling |
top | number | Limit the number of ranked entries |
sort | "cyclomatic" | "cognitive" | "lines" | "severity" | Finding sort order (default cyclomatic) |
complexity, fileScores, coverageGaps, hotspots, targets, score | boolean | Turn on single report sections |
complexityBreakdown | boolean | Add the contributions[] breakdown of decision points to each complexity finding |
css | boolean | Add structural CSS analytics (same as --css) |
ownership, ownershipEmails | boolean + "raw" | "handle" | "anonymized" | "hash" | Ownership-aware hotspots |
effort | "low" | "medium" | "high" | Filter refactoring targets |
since, minCommits | string, number | Git history window for hotspots |
coverage, coverageRoot | string | Istanbul coverage file and its root |
typeAware | { enabled?, projects?, require? } | Type-aware TypeScript analysis, as in the dead-code options. Without a report section, the config rule of the dead-code options applies. |
Structured errors
A rejected promise throws a FallowNodeError. This is a plain Error with extra fields, and it has the same structured errors as the CLI:
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 codes are the same as in the CLI, for example 0 for success, 2 for a generic or validation error, and 7 for a network error (license and cloud operations).
When to use bindings vs the CLI
| Use the bindings | Use the CLI |
|---|---|
| You embed fallow in a long-running Node process (editor, devserver, CI runner) | You run fallow once from a shell script or GitHub Action |
| You already have a Node runtime and want to skip the cost of a subprocess and JSON parsing | You want zero runtime dependencies (native binary via npx fallow) |
| You call many analyses in sequence and do not want to initialize again each time | You want human-readable CLI output, progress bars, or interactive flags |
| You need typed access to the result envelope in TypeScript | You consume the analysis from Rust, Python, Go, or any non-Node language |
The bindings and the CLI run the same analysis engine and return the same data.
Limitations
- The bindings cover the one-shot analyses only. They do not include commands that write to the filesystem:
fix,init,hooks install,hooks uninstall,--save-baseline,license activate, andcoverage setup. detectSimilarCodeis read-only and never does the first model download. For that, runfallow similar-code setup --localthrough the CLI after explicit approval.- There is no baseline or regression support yet. For those workflows, use the CLI flags
--baselineand--save-baseline. - There is no watch mode. For incremental analysis, call the function again. The fallow per-file cache makes repeat runs fast.
- The API is new. Until it is stable, a minor version can have breaking changes.