{ "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "FallowConfig", "description": "User-facing configuration loaded from `.fallowrc.json` or `fallow.toml`.\n\n# Examples\n\n```\nuse fallow_config::FallowConfig;\n\n// Default config has sensible defaults\nlet config = FallowConfig::default();\nassert!(config.entry.is_empty());\nassert!(!config.production);\n\n// Deserialize from JSON\nlet config: FallowConfig = serde_json::from_str(r#\"{\n \"entry\": [\"src/main.ts\"],\n \"production\": true\n}\"#).unwrap();\nassert_eq!(config.entry, vec![\"src/main.ts\"]);\nassert!(config.production);\n```", "type": "object", "properties": { "$schema": { "description": "JSON Schema reference (ignored during deserialization).", "type": [ "string", "null" ], "writeOnly": true }, "extends": { "description": "Base config files to extend from.\n\nSupports three resolution strategies:\n- **Relative paths**: `\"./base.json\"` — resolved relative to the config file.\n- **npm packages**: `\"npm:@co/config\"` — resolved by walking up `node_modules/`.\n Package resolution checks `package.json` `exports`/`main` first, then falls back\n to standard config file names. Subpaths are supported (e.g., `npm:@co/config/strict.json`).\n- **HTTPS URLs**: `\"https://example.com/fallow-base.json\"` — fetched remotely.\n Only HTTPS is supported (no plain HTTP). URL-sourced configs may extend other\n URLs or `npm:` packages, but not relative paths. Only JSON/JSONC format is\n supported for remote configs. Timeout is configurable via\n `FALLOW_EXTENDS_TIMEOUT_SECS` (default: 5s).\n\nBase configs are loaded first, then this config's values override them.\nLater entries in the array override earlier ones.\n\n**Note:** `npm:` resolution uses `node_modules/` directory walk-up and is\nincompatible with Yarn Plug'n'Play (PnP), which has no `node_modules/`.\nURL extends fetch on every run (no caching). For reliable CI, prefer `npm:`\nfor private or critical configs.", "type": "array", "items": { "type": "string" }, "writeOnly": true }, "entry": { "description": "Additional entry point glob patterns.", "type": "array", "items": { "type": "string" }, "default": [] }, "ignorePatterns": { "description": "Glob patterns to ignore from analysis.", "type": "array", "items": { "type": "string" }, "default": [] }, "framework": { "description": "Custom framework definitions (inline plugin definitions).", "type": "array", "items": { "$ref": "#/$defs/ExternalPluginDef" }, "default": [] }, "workspaces": { "description": "Workspace overrides.", "anyOf": [ { "$ref": "#/$defs/WorkspaceConfig" }, { "type": "null" } ], "default": null }, "ignoreDependencies": { "description": "Dependencies to ignore (always considered used and always considered available).\n\nListed dependencies are excluded from both unused dependency and unlisted\ndependency detection. Useful for runtime-provided packages like `bun:sqlite`\nor implicitly available dependencies.", "type": "array", "items": { "type": "string" }, "default": [] }, "ignoreExports": { "description": "Export ignore rules.", "type": "array", "items": { "$ref": "#/$defs/IgnoreExportRule" }, "default": [] }, "usedClassMembers": { "description": "Class member method/property rules that should never be flagged as\nunused. Supports plain member names for global suppression and scoped\nobjects with `extends` / `implements` constraints for framework-invoked\nmethods that should only be suppressed on matching classes.", "type": "array", "items": { "$ref": "#/$defs/UsedClassMemberRule" }, "default": [] }, "duplicates": { "description": "Duplication detection settings.", "$ref": "#/$defs/DuplicatesConfig", "default": { "enabled": true, "mode": "mild", "minTokens": 50, "minLines": 5, "threshold": 0.0, "ignore": [], "skipLocal": false, "crossLanguage": false, "ignoreImports": false, "normalization": {} } }, "health": { "description": "Complexity health metrics settings.", "$ref": "#/$defs/HealthConfig", "default": { "maxCyclomatic": 20, "maxCognitive": 15, "ignore": [], "ownership": { "botPatterns": [ "*\\[bot\\]*", "dependabot*", "renovate*", "github-actions*", "svc-*", "*-service-account*" ], "emailMode": "handle" } } }, "rules": { "description": "Per-issue-type severity rules.", "$ref": "#/$defs/RulesConfig", "default": { "unused-files": "error", "unused-exports": "error", "unused-types": "error", "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", "boundary-violation": "error", "coverage-gaps": "off", "feature-flags": "off", "stale-suppressions": "warn" } }, "boundaries": { "description": "Architecture boundary enforcement configuration.", "$ref": "#/$defs/BoundaryConfig", "default": { "zones": [], "rules": [] } }, "flags": { "description": "Feature flag detection configuration.", "$ref": "#/$defs/FlagsConfig", "default": { "configObjectHeuristics": false } }, "production": { "description": "Production mode: exclude test/dev files, only start/build scripts.", "type": "boolean", "default": false }, "plugins": { "description": "Paths to external plugin files or directories containing plugin files.\n\nSupports TOML, JSON, and JSONC formats.\n\nIn addition to these explicit paths, fallow automatically discovers:\n- `*.toml`, `*.json`, `*.jsonc` files in `.fallow/plugins/`\n- `fallow-plugin-*.{toml,json,jsonc}` files in the project root", "type": "array", "items": { "type": "string" }, "default": [] }, "dynamicallyLoaded": { "description": "Glob patterns for files that are dynamically loaded at runtime\n(plugin directories, locale files, etc.). These files are treated as\nalways-used and will never be flagged as unused.", "type": "array", "items": { "type": "string" }, "default": [] }, "overrides": { "description": "Per-file rule overrides matching oxlint's overrides pattern.", "type": "array", "items": { "$ref": "#/$defs/ConfigOverride" }, "default": [] }, "codeowners": { "description": "Path to a CODEOWNERS file for `--group-by owner`.\n\nWhen unset, fallow auto-probes `CODEOWNERS`, `.github/CODEOWNERS`,\n`.gitlab/CODEOWNERS`, and `docs/CODEOWNERS`. Set this to use a\nnon-standard location.", "type": [ "string", "null" ] }, "publicPackages": { "description": "Workspace package name patterns that are public libraries.\nExports from these packages are not flagged as unused.", "type": "array", "items": { "type": "string" }, "default": [] }, "regression": { "description": "Regression detection baseline embedded in config.\nStores issue counts from a known-good state for CI regression checks.\nPopulated by `--save-regression-baseline` (no path), read by `--fail-on-regression`.", "anyOf": [ { "$ref": "#/$defs/RegressionConfig" }, { "type": "null" } ] }, "sealed": { "description": "Mark this config as sealed: `extends` paths must be file-relative and\nresolve within this config's own directory. `npm:` and `https:` extends\nare rejected. Useful for library publishers and monorepo sub-packages\nthat want to guarantee their config is self-contained and not subject\nto ancestor configs being injected via `extends`.\n\nDiscovery is unaffected (first-match-wins already stops the directory\nwalk at the nearest config). This only constrains `extends`.", "type": "boolean", "default": false } }, "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 (`\"agInit\"`) or a scoped rule that\nonly applies when a class matches specific `extends` / `implements`\nheritage clauses.", "anyOf": [ { "description": "Globally suppress this class member name 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 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" ] }, "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 }, "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": [] }, "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": {} } } }, "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 }, "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" } } } }, "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|hash`.", "$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": "hash" } ] }, "RulesConfig": { "description": "Per-issue-type severity configuration.\n\nControls which issue types cause CI failure, are reported as warnings,\nor are suppressed entirely. All 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" }, "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" }, "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" } } }, "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.\n\nDefines zones (directory groupings) and rules (which zones may import from which).\nOptionally uses a built-in preset as a starting point.\n\n# Examples\n\n```\nuse fallow_config::BoundaryConfig;\n\nlet json = r#\"{\n \"zones\": [\n { \"name\": \"ui\", \"patterns\": [\"src/components/**\"] },\n { \"name\": \"db\", \"patterns\": [\"src/db/**\"] }\n ],\n \"rules\": [\n { \"from\": \"ui\", \"allow\": [\"db\"] }\n ]\n}\"#;\nlet config: BoundaryConfig = serde_json::from_str(json).unwrap();\nassert_eq!(config.zones.len(), 2);\nassert_eq!(config.rules.len(), 1);\n```\n\nUsing a preset:\n\n```\nuse fallow_config::BoundaryConfig;\n\nlet json = r#\"{ \"preset\": \"layered\" }\"#;\nlet mut config: BoundaryConfig = serde_json::from_str(json).unwrap();\nconfig.expand(\"src\");\nassert_eq!(config.zones.len(), 4);\nassert_eq!(config.rules.len(), 4);\n```", "type": "object", "properties": { "preset": { "description": "Built-in architecture preset. When set, expands into default zones and rules.\nUser-defined zones and rules merge on top: zones with the same name replace\nthe preset zone; rules with the same `from` replace the preset rule.\nPreset patterns use `{rootDir}/{zone}/**` where rootDir is auto-detected\nfrom tsconfig.json (falls back to `src`).\nNote: preset patterns are flat (`src//**`). For monorepos with\nper-package source directories, define zones explicitly instead.", "anyOf": [ { "$ref": "#/$defs/BoundaryPreset" }, { "type": "null" } ] }, "zones": { "description": "Named zones mapping directory patterns to architectural layers.", "type": "array", "items": { "$ref": "#/$defs/BoundaryZone" }, "default": [] }, "rules": { "description": "Import rules between zones. A zone with a rule entry can only import\nfrom the listed zones (plus itself). A zone without a rule entry is unrestricted.", "type": "array", "items": { "$ref": "#/$defs/BoundaryRule" }, "default": [] } } }, "BoundaryPreset": { "description": "Built-in architecture presets.\n\nEach preset expands into a set of zones and import rules for a common\narchitecture pattern. User-defined zones and rules merge on top of the\npreset defaults (zones with the same name replace the preset zone;\nrules with the same `from` replace the preset rule).\n\n# Examples\n\n```\nuse fallow_config::BoundaryPreset;\n\nlet preset: BoundaryPreset = serde_json::from_str(r#\"\"layered\"\"#).unwrap();\nassert!(matches!(preset, BoundaryPreset::Layered));\n```", "oneOf": [ { "description": "Classic layered architecture: presentation → application → domain ← infrastructure.\nInfrastructure may also import from application (common in DI frameworks).", "type": "string", "const": "layered" }, { "description": "Hexagonal / ports-and-adapters: adapters → ports → domain.", "type": "string", "const": "hexagonal" }, { "description": "Feature-Sliced Design: app > pages > widgets > features > entities > shared.\nEach layer may only import from layers below it.", "type": "string", "const": "feature-sliced" }, { "description": "Bulletproof React: app → features → shared + server.\nFeature modules are isolated from each other; shared utilities and server\ninfrastructure form the base layers.", "type": "string", "const": "bulletproof" } ] }, "BoundaryZone": { "description": "A named zone grouping files by directory pattern.", "type": "object", "properties": { "name": { "description": "Zone identifier referenced in rules (e.g., `\"ui\"`, `\"database\"`, `\"shared\"`).", "type": "string" }, "patterns": { "description": "Glob patterns (relative to project root) that define zone membership.\nA file belongs to the first zone whose pattern matches.", "type": "array", "items": { "type": "string" } }, "root": { "description": "Optional subtree scope. When set, patterns are relative to this directory\ninstead of the project root. Useful for monorepos with per-package boundaries.\nReserved for future use — currently ignored by the detector.", "type": [ "string", "null" ] } }, "required": [ "name", "patterns" ] }, "BoundaryRule": { "description": "An import rule between zones.", "type": "object", "properties": { "from": { "description": "The zone this rule applies to (the importing side).", "type": "string" }, "allow": { "description": "Zones that `from` is allowed to import from. Self-imports are always allowed.\nAn empty list means the zone may not import from any other zone.", "type": "array", "items": { "type": "string" }, "default": [] } }, "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` 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 (LaunchDarkly, Statsig, Unleash, GrowthBook).", "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" ] }, "ConfigOverride": { "description": "Per-file override entry.", "type": "object", "properties": { "files": { "description": "Glob patterns to match files against (relative to config file location).", "type": "array", "items": { "type": "string" } }, "rules": { "description": "Partial rules — only specified fields override the base 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" } ] }, "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" } ] }, "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" } ] } } }, "RegressionConfig": { "description": "Regression baseline counts, embedded in the config file.\n\nWhen `--fail-on-regression` is used without `--regression-baseline `,\nfallow reads the baseline from this config section.\nWhen `--save-regression-baseline` is used without a path argument,\nfallow writes the baseline into the config file.", "type": "object", "properties": { "baseline": { "description": "Dead code issue counts baseline.", "anyOf": [ { "$ref": "#/$defs/RegressionBaseline" }, { "type": "null" } ] } } }, "RegressionBaseline": { "description": "Per-type issue counts for regression comparison.", "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 }, "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 } } } } }