{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "FallowConfig",
"type": "object",
"properties": {
"$schema": {
"type": [
"string",
"null"
],
"writeOnly": true
},
"extends": {
"type": "array",
"items": {
"type": "string"
},
"writeOnly": true
},
"entry": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"ignorePatterns": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"framework": {
"type": "array",
"items": {
"$ref": "#/$defs/ExternalPluginDef"
},
"default": []
},
"workspaces": {
"anyOf": [
{
"$ref": "#/$defs/WorkspaceConfig"
},
{
"type": "null"
}
],
"default": null
},
"ignoreDependencies": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"ignoreUnresolvedImports": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"ignoreExports": {
"type": "array",
"items": {
"$ref": "#/$defs/IgnoreExportRule"
},
"default": []
},
"ignoreCatalogReferences": {
"type": "array",
"items": {
"$ref": "#/$defs/IgnoreCatalogReferenceRule"
}
},
"ignoreDependencyOverrides": {
"type": "array",
"items": {
"$ref": "#/$defs/IgnoreDependencyOverrideRule"
}
},
"ignoreExportsUsedInFile": {
"$ref": "#/$defs/IgnoreExportsUsedInFileConfig",
"default": false
},
"ignoreDecorators": {
"type": "array",
"items": {
"type": "string"
}
},
"usedClassMembers": {
"type": "array",
"items": {
"$ref": "#/$defs/UsedClassMemberRule"
},
"default": []
},
"duplicates": {
"$ref": "#/$defs/DuplicatesConfig",
"default": {
"enabled": true,
"mode": "mild",
"minTokens": 50,
"minLines": 5,
"minOccurrences": 2,
"threshold": 0.0,
"ignore": [],
"ignoreDefaults": true,
"skipLocal": false,
"crossLanguage": false,
"ignoreImports": false,
"normalization": {},
"minCorpusSizeForShingleFilter": 1024,
"minCorpusSizeForTokenCache": 5000
}
},
"health": {
"$ref": "#/$defs/HealthConfig",
"default": {
"maxCyclomatic": 20,
"maxCognitive": 15,
"maxCrap": 30.0,
"crapRefactorBand": 5,
"ignore": [],
"ownership": {
"botPatterns": [
"*\\[bot\\]*",
"dependabot*",
"renovate*",
"github-actions*",
"svc-*",
"*-service-account*"
],
"emailMode": "handle"
},
"suggestInlineSuppression": true
}
},
"rules": {
"$ref": "#/$defs/RulesConfig",
"default": {
"unused-files": "error",
"unused-exports": "error",
"unused-types": "error",
"private-type-leaks": "off",
"unused-dependencies": "error",
"unused-dev-dependencies": "warn",
"unused-optional-dependencies": "warn",
"unused-enum-members": "error",
"unused-class-members": "error",
"unresolved-imports": "error",
"unlisted-dependencies": "error",
"duplicate-exports": "error",
"type-only-dependencies": "warn",
"test-only-dependencies": "warn",
"circular-dependencies": "error",
"re-export-cycle": "warn",
"boundary-violation": "error",
"coverage-gaps": "off",
"feature-flags": "off",
"stale-suppressions": "warn",
"unused-catalog-entries": "warn",
"empty-catalog-groups": "warn",
"unresolved-catalog-references": "error",
"unused-dependency-overrides": "warn",
"misconfigured-dependency-overrides": "error",
"security-client-server-leak": "off",
"security-sink": "off"
}
},
"boundaries": {
"$ref": "#/$defs/BoundaryConfig",
"default": {
"zones": [],
"rules": []
}
},
"flags": {
"$ref": "#/$defs/FlagsConfig",
"default": {
"configObjectHeuristics": false
}
},
"security": {
"$ref": "#/$defs/SecurityConfig",
"default": {}
},
"fix": {
"$ref": "#/$defs/FixConfig",
"default": {
"catalog": {
"deletePrecedingComments": "auto"
}
}
},
"resolve": {
"$ref": "#/$defs/ResolveConfig",
"default": {}
},
"production": {
"$ref": "#/$defs/ProductionConfig",
"default": false
},
"plugins": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"dynamicallyLoaded": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"overrides": {
"type": "array",
"items": {
"$ref": "#/$defs/ConfigOverride"
},
"default": []
},
"codeowners": {
"type": [
"string",
"null"
]
},
"publicPackages": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"regression": {
"anyOf": [
{
"$ref": "#/$defs/RegressionConfig"
},
{
"type": "null"
}
]
},
"audit": {
"$ref": "#/$defs/AuditConfig"
},
"sealed": {
"type": "boolean",
"default": false
},
"includeEntryExports": {
"type": "boolean",
"default": false
},
"autoImports": {
"type": "boolean",
"default": false
},
"cache": {
"$ref": "#/$defs/CacheConfig"
}
},
"additionalProperties": false,
"$defs": {
"ExternalPluginDef": {
"description": "A declarative plugin definition loaded from a standalone file or inline config.\n\nExternal plugins provide the same static pattern capabilities as built-in\nplugins (entry points, always-used files, used exports, tooling dependencies),\nbut are defined in standalone files or inline in the fallow config rather than\ncompiled Rust code.\n\nThey cannot do AST-based config parsing (`resolve_config()`), but cover the\nvast majority of framework integration use cases.\n\nSupports JSONC, JSON, and TOML formats. All use camelCase field names.\n\n```json\n{\n \"$schema\": \"https://raw.githubusercontent.com/fallow-rs/fallow/main/plugin-schema.json\",\n \"name\": \"my-framework\",\n \"enablers\": [\"my-framework\", \"@my-framework/core\"],\n \"entryPoints\": [\"src/routes/**/*.{ts,tsx}\"],\n \"configPatterns\": [\"my-framework.config.{ts,js}\"],\n \"alwaysUsed\": [\"src/setup.ts\"],\n \"toolingDependencies\": [\"my-framework-cli\"],\n \"usedExports\": [\n { \"pattern\": \"src/routes/**/*.{ts,tsx}\", \"exports\": [\"default\", \"loader\", \"action\"] }\n ]\n}\n```",
"type": "object",
"properties": {
"name": {
"description": "Unique name for this plugin.",
"type": "string"
},
"detection": {
"description": "Rich detection logic (dependency checks, file existence, boolean combinators).\nTakes priority over `enablers` when set.",
"anyOf": [
{
"$ref": "#/$defs/PluginDetection"
},
{
"type": "null"
}
],
"default": null
},
"enablers": {
"description": "Package names that activate this plugin when found in package.json.\nSupports exact matches and prefix patterns (ending with `/`).\nOnly used when `detection` is not set.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"entryPoints": {
"description": "Glob patterns for entry point files.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"entryPointRole": {
"description": "Coverage role for `entryPoints`.\n\nDefaults to `support`. Set to `runtime` for application entry points\nor `test` for test framework entry points.",
"$ref": "#/$defs/EntryPointRole",
"default": "support"
},
"configPatterns": {
"description": "Glob patterns for config files (marked as always-used when active).",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"alwaysUsed": {
"description": "Files that are always considered \"used\" when this plugin is active.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"toolingDependencies": {
"description": "Dependencies that are tooling (used via CLI/config, not source imports).\nThese should not be flagged as unused devDependencies.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"usedExports": {
"description": "Exports that are always considered used for matching file patterns.",
"type": "array",
"items": {
"$ref": "#/$defs/ExternalUsedExport"
},
"default": []
},
"usedClassMembers": {
"description": "Class member method/property rules the framework invokes at runtime.\nSupports plain member names for global suppression and scoped objects\nwith `extends` / `implements` constraints when the method name is too\ncommon to suppress across the whole workspace.",
"type": "array",
"items": {
"$ref": "#/$defs/UsedClassMemberRule"
},
"default": []
}
},
"required": [
"name"
]
},
"PluginDetection": {
"description": "How to detect if a plugin should be activated.\n\nWhen set on an `ExternalPluginDef`, this takes priority over `enablers`.\nSupports dependency checks, file existence checks, and boolean combinators.",
"oneOf": [
{
"description": "Plugin detected if this package is in dependencies.",
"type": "object",
"properties": {
"package": {
"type": "string"
},
"type": {
"type": "string",
"const": "dependency"
}
},
"required": [
"type",
"package"
]
},
{
"description": "Plugin detected if this file pattern matches.",
"type": "object",
"properties": {
"pattern": {
"type": "string"
},
"type": {
"type": "string",
"const": "fileExists"
}
},
"required": [
"type",
"pattern"
]
},
{
"description": "All conditions must be true.",
"type": "object",
"properties": {
"conditions": {
"type": "array",
"items": {
"$ref": "#/$defs/PluginDetection"
}
},
"type": {
"type": "string",
"const": "all"
}
},
"required": [
"type",
"conditions"
]
},
{
"description": "Any condition must be true.",
"type": "object",
"properties": {
"conditions": {
"type": "array",
"items": {
"$ref": "#/$defs/PluginDetection"
}
},
"type": {
"type": "string",
"const": "any"
}
},
"required": [
"type",
"conditions"
]
}
]
},
"EntryPointRole": {
"description": "How a plugin's discovered entry points contribute to coverage reachability.",
"oneOf": [
{
"description": "Runtime/application roots that should count toward runtime reachability.",
"type": "string",
"const": "runtime"
},
{
"description": "Test roots that should count toward test reachability.",
"type": "string",
"const": "test"
},
{
"description": "Support/setup/config roots that should keep files alive but not count as runtime/test.",
"type": "string",
"const": "support"
}
]
},
"ExternalUsedExport": {
"description": "Exports considered used for files matching a pattern.",
"type": "object",
"properties": {
"pattern": {
"description": "Glob pattern for files.",
"type": "string"
},
"exports": {
"description": "Export names always considered used.",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"pattern",
"exports"
]
},
"UsedClassMemberRule": {
"description": "A `usedClassMembers` entry from config or an external plugin.\n\nSupports either a plain member name or glob pattern (`\"agInit\"`,\n`\"enter*\"`) or a scoped rule that only applies when a class matches\nspecific `extends` / `implements` heritage clauses.",
"anyOf": [
{
"description": "Globally suppress this class member name or glob pattern for all classes.",
"type": "string"
},
{
"description": "Suppress these class member names only for matching classes.",
"$ref": "#/$defs/ScopedUsedClassMemberRule"
}
]
},
"ScopedUsedClassMemberRule": {
"description": "A heritage-constrained `usedClassMembers` rule.",
"type": "object",
"properties": {
"extends": {
"description": "Only apply when the class extends this parent class name.",
"type": [
"string",
"null"
]
},
"implements": {
"description": "Only apply when the class implements this interface name.",
"type": [
"string",
"null"
]
},
"members": {
"description": "Member names or glob patterns that should be treated as framework-used.",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"members"
]
},
"WorkspaceConfig": {
"description": "Workspace configuration for monorepo support.",
"type": "object",
"properties": {
"patterns": {
"description": "Additional workspace patterns (beyond what's in root package.json).",
"type": "array",
"items": {
"type": "string"
},
"default": []
}
}
},
"IgnoreExportRule": {
"description": "Rule for ignoring specific exports.",
"type": "object",
"properties": {
"file": {
"description": "Glob pattern for files.",
"type": "string"
},
"exports": {
"description": "Export names to ignore (`*` for all).",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"file",
"exports"
]
},
"IgnoreCatalogReferenceRule": {
"description": "Rule for suppressing an `unresolved-catalog-reference` finding.",
"type": "object",
"properties": {
"package": {
"type": "string"
},
"catalog": {
"type": [
"string",
"null"
]
},
"consumer": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"package"
]
},
"IgnoreDependencyOverrideRule": {
"description": "Rule for suppressing dependency-override findings.",
"type": "object",
"properties": {
"package": {
"type": "string"
},
"source": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"package"
]
},
"IgnoreExportsUsedInFileConfig": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/$defs/IgnoreExportsUsedInFileByKind"
}
]
},
"IgnoreExportsUsedInFileByKind": {
"type": "object",
"properties": {
"type": {
"type": "boolean",
"default": false
},
"interface": {
"type": "boolean",
"default": false
}
}
},
"DuplicatesConfig": {
"description": "Configuration for code duplication detection.",
"type": "object",
"properties": {
"enabled": {
"description": "Whether duplication detection is enabled.",
"type": "boolean",
"default": true
},
"mode": {
"description": "Detection mode: strict, mild, weak, or semantic.",
"$ref": "#/$defs/DetectionMode",
"default": "mild"
},
"minTokens": {
"description": "Minimum number of tokens for a clone.",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 50
},
"minLines": {
"description": "Minimum number of lines for a clone.",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 5
},
"minOccurrences": {
"description": "Minimum number of occurrences (instances of the same clone) before a\ngroup is reported. Defaults to 2 (every duplicated pair is reported).\nRaise this to focus on widespread copy-paste worth refactoring and skip\ncontext-sensitive pairs.",
"type": "integer",
"format": "uint",
"minimum": 2,
"default": 2
},
"threshold": {
"description": "Maximum allowed duplication percentage (0 = no limit).",
"type": "number",
"format": "double",
"default": 0.0
},
"ignore": {
"description": "Additional ignore patterns for duplication analysis.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"ignoreDefaults": {
"description": "Merge built-in generated-framework ignore patterns with `ignore`.\n\nSet to `false` to use only the user-provided `ignore` list.",
"type": "boolean",
"default": true
},
"skipLocal": {
"description": "Only report cross-directory duplicates.",
"type": "boolean",
"default": false
},
"crossLanguage": {
"description": "Enable cross-language clone detection by stripping type annotations.\n\nWhen enabled, TypeScript type annotations (parameter types, return types,\ngenerics, interfaces, type aliases) are stripped from the token stream,\nallowing detection of clones between `.ts` and `.js` files.",
"type": "boolean",
"default": false
},
"ignoreImports": {
"description": "Exclude ES `import` declarations from clone detection.\n\nWhen enabled, all `import` statements (value imports, type imports, and\nside-effect imports) are stripped from the token stream before clone\ndetection. This reduces noise from sorted import blocks that naturally\nlook similar across files. Only affects ES `import` declarations;\nCommonJS `require()` calls are not filtered.",
"type": "boolean",
"default": false
},
"normalization": {
"description": "Fine-grained normalization overrides on top of the detection mode.",
"$ref": "#/$defs/NormalizationConfig",
"default": {}
},
"minCorpusSizeForShingleFilter": {
"description": "Minimum tokenized file count before focused duplicate analysis prefilters\nunchanged files with k-token shingles.",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 1024
},
"minCorpusSizeForTokenCache": {
"description": "Minimum source file count before the persistent duplication token cache\nactivates. Below this threshold the cache load/save overhead exceeds the\ntokenize savings, so the cache stays disabled even when not running with\n`--no-cache`.",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 5000
}
}
},
"DetectionMode": {
"description": "Detection mode controlling how aggressively tokens are normalized.\n\nSince fallow uses AST-based tokenization (not lexer-based), whitespace and\ncomments are inherently absent from the token stream. The `Strict` and `Mild`\nmodes are currently equivalent. `Weak` mode additionally blinds string\nliterals. `Semantic` mode blinds all identifiers and literal values for\nType-2 (renamed variable) clone detection.",
"oneOf": [
{
"description": "All tokens preserved including identifier names and literal values (Type-1 only).",
"type": "string",
"const": "strict"
},
{
"description": "Default mode -- equivalent to strict for AST-based tokenization.",
"type": "string",
"const": "mild"
},
{
"description": "Blind string literal values (structure-preserving).",
"type": "string",
"const": "weak"
},
{
"description": "Blind all identifiers and literal values for structural (Type-2) detection.",
"type": "string",
"const": "semantic"
}
]
},
"NormalizationConfig": {
"description": "Fine-grained normalization overrides.\n\nEach option, when set to `Some(true)`, forces that normalization regardless of\nthe detection mode. When set to `Some(false)`, it forces preservation. When\n`None`, the detection mode's default behavior applies.",
"type": "object",
"properties": {
"ignoreIdentifiers": {
"description": "Blind all identifiers (variable names, function names, etc.) to the same hash.\nDefault in `semantic` mode.",
"type": [
"boolean",
"null"
]
},
"ignoreStringValues": {
"description": "Blind string literal values to the same hash.\nDefault in `weak` and `semantic` modes.",
"type": [
"boolean",
"null"
]
},
"ignoreNumericValues": {
"description": "Blind numeric literal values to the same hash.\nDefault in `semantic` mode.",
"type": [
"boolean",
"null"
]
}
}
},
"HealthConfig": {
"description": "Configuration for complexity health metrics (`fallow health`).",
"type": "object",
"properties": {
"maxCyclomatic": {
"description": "Maximum allowed cyclomatic complexity per function (default: 20).\nFunctions exceeding this threshold are reported.",
"type": "integer",
"format": "uint16",
"minimum": 0,
"maximum": 65535,
"default": 20
},
"maxCognitive": {
"description": "Maximum allowed cognitive complexity per function (default: 15).\nFunctions exceeding this threshold are reported.",
"type": "integer",
"format": "uint16",
"minimum": 0,
"maximum": 65535,
"default": 15
},
"maxCrap": {
"description": "Maximum allowed CRAP (Change Risk Anti-Patterns) score per function\n(default: 30.0). CRAP combines cyclomatic complexity with test\ncoverage: high complexity plus low coverage produces a high CRAP\nscore. Functions meeting or exceeding this threshold are reported.\nUse `--coverage` with Istanbul data for accurate per-function CRAP;\notherwise fallow estimates coverage from the module graph.",
"type": "number",
"format": "double",
"default": 30.0
},
"crapRefactorBand": {
"description": "Band below `maxCyclomatic` where CRAP-only findings also receive a\nsecondary `refactor-function` action (default: 5). Set to `0` to only\nsuggest refactoring when cyclomatic already meets the configured\nthreshold.",
"type": "integer",
"format": "uint16",
"minimum": 0,
"maximum": 65535,
"default": 5
},
"ignore": {
"description": "Glob patterns to exclude from complexity analysis.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"ownership": {
"description": "Ownership analysis configuration. Controls bot filtering and email\nprivacy mode for `--ownership` output.",
"$ref": "#/$defs/OwnershipConfig",
"default": {
"botPatterns": [
"*\\[bot\\]*",
"dependabot*",
"renovate*",
"github-actions*",
"svc-*",
"*-service-account*"
],
"emailMode": "handle"
}
},
"suggestInlineSuppression": {
"description": "Whether health JSON output emits `suppress-line` action hints\nalongside complexity findings (default: `true`). Set to `false` to\nopt out across the project: useful for teams that manage suppressions\nexclusively through `// fallow-ignore-*` comments authored by hand or\nthrough the `fallow.suppress` LSP code action, but who do not want\nCI-driven `suppress-line` action hints in their JSON output.\n`--baseline` activates auto-omission regardless of this setting,\nsince baseline files are a separate suppression mechanism.",
"type": "boolean",
"default": true
}
}
},
"OwnershipConfig": {
"description": "Configuration for ownership analysis (`fallow health --hotspots --ownership`).",
"type": "object",
"properties": {
"botPatterns": {
"description": "Glob patterns (matched against the author email local-part) that\nidentify bot or service-account commits to exclude from ownership\nsignals. Overrides the defaults entirely when set.",
"type": "array",
"items": {
"type": "string"
},
"default": [
"*\\[bot\\]*",
"dependabot*",
"renovate*",
"github-actions*",
"svc-*",
"*-service-account*"
]
},
"emailMode": {
"description": "Privacy mode for emitted author emails. Defaults to `handle`.\nOverride on the CLI via `--ownership-emails=raw|handle|anonymized`.\nThe legacy spelling `hash` is still accepted for compatibility.",
"$ref": "#/$defs/EmailMode",
"default": "handle"
}
}
},
"EmailMode": {
"description": "Privacy mode for author emails emitted in ownership output.\n\nDefaults to `handle` (local-part only, no domain) so SARIF and JSON\nartifacts do not leak raw email addresses into CI pipelines.",
"oneOf": [
{
"description": "Show the raw email address as it appears in git history.\nUse for public repositories where history is already exposed.",
"type": "string",
"const": "raw"
},
{
"description": "Show the local-part only (before the `@`). Mailmap-resolved where possible.\nDefault. Balances readability and privacy.",
"type": "string",
"const": "handle"
},
{
"description": "Show a stable `xxh3:<16hex>` pseudonym derived from the raw email.\nNon-cryptographic; suitable to keep raw emails out of CI artifacts\n(SARIF, code-scanning uploads) but not as a security primitive:\na known list of org emails can be brute-forced into a rainbow table.\nUse in regulated environments where even local-parts are sensitive.",
"type": "string",
"const": "anonymized"
},
{
"description": "Legacy spelling for [`EmailMode::Anonymized`].",
"type": "string",
"const": "hash"
}
]
},
"RulesConfig": {
"description": "Per-issue-type severity configuration.\n\nControls which issue types cause CI failure, are reported as warnings,\nor are suppressed entirely. Most fields default to `Severity::Error`.\n\nRule names use kebab-case in config files (e.g., `\"unused-files\": \"error\"`).",
"type": "object",
"properties": {
"unused-files": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-exports": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-types": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"private-type-leaks": {
"$ref": "#/$defs/Severity",
"default": "off"
},
"unused-dependencies": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-dev-dependencies": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"unused-optional-dependencies": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"unused-enum-members": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-class-members": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unresolved-imports": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unlisted-dependencies": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"duplicate-exports": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"type-only-dependencies": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"test-only-dependencies": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"circular-dependencies": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"re-export-cycle": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"boundary-violation": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"coverage-gaps": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"feature-flags": {
"$ref": "#/$defs/Severity",
"default": "off"
},
"stale-suppressions": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"unused-catalog-entries": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"empty-catalog-groups": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"unresolved-catalog-references": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-dependency-overrides": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"misconfigured-dependency-overrides": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"security-client-server-leak": {
"description": "Opt-in (default off): a `\"use client\"` file that transitively imports a\nmodule reading a non-public `process.env` secret. Surfaced only by\n`fallow security`; never under bare `fallow` or the `audit` gate.",
"$ref": "#/$defs/Severity",
"default": "off"
},
"security-sink": {
"description": "Opt-in (default off): a syntactic tainted-sink candidate matched against\nthe data-driven catalogue (`security_matchers.toml`). ONE knob gates ALL\ncatalogue categories. Surfaced only by `fallow security`; never under\nbare `fallow` or the `audit` gate.",
"$ref": "#/$defs/Severity",
"default": "off"
}
}
},
"Severity": {
"description": "Severity level for rules.\n\nControls whether an issue type causes CI failure (`error`), is reported\nwithout failing (`warn`), or is suppressed entirely (`off`).",
"oneOf": [
{
"description": "Report and fail CI (non-zero exit code).",
"type": "string",
"const": "error"
},
{
"description": "Report but don't fail CI.",
"type": "string",
"const": "warn"
},
{
"description": "Don't detect or report.",
"type": "string",
"const": "off"
}
]
},
"BoundaryConfig": {
"description": "Architecture boundary configuration.",
"type": "object",
"properties": {
"preset": {
"description": "Optional built-in preset.",
"anyOf": [
{
"$ref": "#/$defs/BoundaryPreset"
},
{
"type": "null"
}
]
},
"zones": {
"description": "Zone definitions.",
"type": "array",
"items": {
"$ref": "#/$defs/BoundaryZone"
},
"default": []
},
"rules": {
"description": "Zone import rules.",
"type": "array",
"items": {
"$ref": "#/$defs/BoundaryRule"
},
"default": []
}
}
},
"BoundaryPreset": {
"description": "Built-in architecture presets.",
"oneOf": [
{
"description": "Layered architecture.",
"type": "string",
"const": "layered"
},
{
"description": "Hexagonal / ports-and-adapters.",
"type": "string",
"const": "hexagonal"
},
{
"description": "Feature-Sliced Design.",
"type": "string",
"const": "feature-sliced"
},
{
"description": "Bulletproof React.",
"type": "string",
"const": "bulletproof"
}
]
},
"BoundaryZone": {
"description": "A zone grouping files by directory pattern.",
"type": "object",
"properties": {
"name": {
"description": "Zone name.",
"type": "string"
},
"patterns": {
"description": "Membership patterns.",
"type": "array",
"items": {
"type": "string"
}
},
"autoDiscover": {
"description": "Directories whose children become zones.",
"type": "array",
"items": {
"type": "string"
}
},
"root": {
"description": "Optional subtree scope.",
"type": [
"string",
"null"
]
}
},
"required": [
"name"
]
},
"BoundaryRule": {
"description": "An import rule between zones.",
"type": "object",
"properties": {
"from": {
"description": "Source zone.",
"type": "string"
},
"allow": {
"description": "Allowed target zones.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"allowTypeOnly": {
"description": "Allowed type-only targets.",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"from"
]
},
"FlagsConfig": {
"description": "Feature flag detection configuration.\n\nControls which patterns fallow uses to detect feature flags in source code.\nConfigured via the `flags` section in `.fallowrc.json`, `.fallowrc.jsonc`, `fallow.toml`, or `.fallow.toml`.\n\n# Examples\n\n```json\n{\n \"flags\": {\n \"sdkPatterns\": [\n { \"function\": \"useFlag\", \"nameArg\": 0, \"provider\": \"LaunchDarkly\" }\n ],\n \"envPrefixes\": [\"FEATURE_\", \"NEXT_PUBLIC_ENABLE_\"],\n \"configObjectHeuristics\": false\n }\n}\n```",
"type": "object",
"properties": {
"sdkPatterns": {
"description": "Additional SDK call patterns to detect as feature flags.\nThese are merged with the built-in patterns for common providers\nincluding LaunchDarkly, Statsig, Unleash, GrowthBook, Split, PostHog,\nVercel Flags, ConfigCat, Flagsmith, Optimizely, and Eppo.",
"type": "array",
"items": {
"$ref": "#/$defs/SdkPattern"
}
},
"envPrefixes": {
"description": "Environment variable prefixes that indicate feature flags.\nMerged with built-in prefixes. Only `process.env.*` accesses matching\nthese prefixes are reported as feature flags.",
"type": "array",
"items": {
"type": "string"
}
},
"configObjectHeuristics": {
"description": "Enable config object heuristic detection.\nWhen true, property accesses on objects whose name contains \"feature\",\n\"flag\", or \"toggle\" are reported as low-confidence feature flags.\nDefault: false (opt-in due to higher false positive rate).",
"type": "boolean",
"default": false
}
}
},
"SdkPattern": {
"description": "A custom SDK call pattern for feature flag detection.\n\nDescribes a function call that evaluates a feature flag, e.g.,\n`useFlag('new-checkout')` or `client.getFeatureValue('parser', false)`.",
"type": "object",
"properties": {
"function": {
"description": "Function name to match (e.g., `\"useFlag\"`, `\"variation\"`).",
"type": "string"
},
"nameArg": {
"description": "Zero-based index of the argument containing the flag name.",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"provider": {
"description": "Optional SDK/provider label shown in output (e.g., `\"LaunchDarkly\"`).",
"type": [
"string",
"null"
]
}
},
"required": [
"function"
]
},
"SecurityConfig": {
"description": "Scopes the data-driven security matcher catalogue used by `fallow security`.\nAn absent block (or both `include`/`exclude` unset) admits every category.",
"type": "object",
"properties": {
"categories": {
"description": "Include/exclude filter over catalogue category ids (e.g. `dangerous-html`).",
"anyOf": [
{
"$ref": "#/$defs/SecurityCategories"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"SecurityCategories": {
"description": "Include/exclude lists scoping the active security matcher categories. When\n`include` is set, only those categories are active; `exclude` removes\ncategories from the admitted set. Both unset admits every category.",
"type": "object",
"properties": {
"include": {
"description": "Catalogue category ids to admit. When set, all others are excluded.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"exclude": {
"description": "Catalogue category ids to remove from the admitted set.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"FixConfig": {
"type": "object",
"properties": {
"catalog": {
"$ref": "#/$defs/CatalogFixConfig",
"default": {
"deletePrecedingComments": "auto"
}
}
}
},
"CatalogFixConfig": {
"type": "object",
"properties": {
"deletePrecedingComments": {
"$ref": "#/$defs/CatalogPrecedingCommentPolicy",
"default": "auto"
}
}
},
"CatalogPrecedingCommentPolicy": {
"type": "string",
"enum": [
"auto",
"always",
"never"
]
},
"ResolveConfig": {
"description": "Module resolver configuration.\n\nControls how fallow resolves import specifiers against package.json\n`exports` / `imports` fields and tsconfig paths. Configured via the\n`resolve` section in `.fallowrc.json`, `.fallowrc.jsonc`, `fallow.toml`, or `.fallow.toml`.\n\n# Examples\n\n```json\n{\n \"resolve\": {\n \"conditions\": [\"development\", \"worker\"]\n }\n}\n```",
"type": "object",
"properties": {
"conditions": {
"description": "Additional export/import condition names to honor during module\nresolution. Merged with fallow's built-in conditions (`development`,\n`import`, `require`, `default`, `types`, `node`; plus `react-native`\nand `browser` when the React Native or Expo plugin is active).\n\nUser conditions are matched with higher priority than the baseline,\nso a package.json `exports` entry like:\n\n```json\n{ \"./api\": { \"worker\": \"./src/api.worker.ts\", \"import\": \"./dist/api.js\" } }\n```\n\nresolves to the `worker` branch when `\"worker\"` is listed here.\n\nSee <https://nodejs.org/api/packages.html#community-conditions-definitions>\nfor the set of community-defined conditions.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"ProductionConfig": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/$defs/PerAnalysisProductionConfig"
}
]
},
"PerAnalysisProductionConfig": {
"type": "object",
"properties": {
"deadCode": {
"type": "boolean",
"default": false
},
"health": {
"type": "boolean",
"default": false
},
"dupes": {
"type": "boolean",
"default": false
}
},
"additionalProperties": false
},
"ConfigOverride": {
"description": "Per-file override entry.",
"type": "object",
"properties": {
"files": {
"type": "array",
"items": {
"type": "string"
}
},
"rules": {
"$ref": "#/$defs/PartialRulesConfig",
"default": {}
}
},
"required": [
"files"
]
},
"PartialRulesConfig": {
"description": "Partial per-issue-type severity for overrides. All fields optional.",
"type": "object",
"properties": {
"unused-files": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unused-exports": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unused-types": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"private-type-leaks": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unused-dependencies": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unused-dev-dependencies": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unused-optional-dependencies": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unused-enum-members": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unused-class-members": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unresolved-imports": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unlisted-dependencies": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"duplicate-exports": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"type-only-dependencies": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"test-only-dependencies": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"circular-dependencies": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"re-export-cycle": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"boundary-violation": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"coverage-gaps": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"feature-flags": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"stale-suppressions": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unused-catalog-entries": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"empty-catalog-groups": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unresolved-catalog-references": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"unused-dependency-overrides": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"misconfigured-dependency-overrides": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"security-client-server-leak": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
},
"security-sink": {
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
}
}
},
"RegressionConfig": {
"type": "object",
"properties": {
"baseline": {
"anyOf": [
{
"$ref": "#/$defs/RegressionBaseline"
},
{
"type": "null"
}
]
}
}
},
"RegressionBaseline": {
"type": "object",
"properties": {
"totalIssues": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"unusedFiles": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"unusedExports": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"unusedTypes": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"unusedDependencies": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"unusedDevDependencies": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"unusedOptionalDependencies": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"unusedEnumMembers": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"unusedClassMembers": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"unresolvedImports": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"unlistedDependencies": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"duplicateExports": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"circularDependencies": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"reExportCycles": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"typeOnlyDependencies": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"testOnlyDependencies": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"boundaryViolations": {
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
}
}
},
"AuditConfig": {
"type": "object",
"properties": {
"gate": {
"$ref": "#/$defs/AuditGate"
},
"deadCodeBaseline": {
"type": [
"string",
"null"
]
},
"healthBaseline": {
"type": [
"string",
"null"
]
},
"dupesBaseline": {
"type": [
"string",
"null"
]
},
"cacheMaxAgeDays": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0
}
}
},
"AuditGate": {
"type": "string",
"enum": [
"new-only",
"all"
]
},
"CacheConfig": {
"type": "object",
"properties": {
"maxSizeMb": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0
}
},
"additionalProperties": false
}
}
}