Ship it
Configuration
Every project has a featurevisor.config.js at its root, next to package.json. Every option has a default, so an empty object is a valid configuration. This page lists them all.
A typical configuration#
The project from the Quick start, once Targets gives it a datafile for web and one for mobile, needs only its tags:
module.exports = { tags: ['web', 'mobile'],}A project with environments lists them too:
module.exports = { tags: ['web', 'mobile'], environments: ['staging', 'production'],}The scaffold's file starts with a @type comment, /** @type {import('@featurevisor/core').ProjectConfig} */, so editors suggest every option as you type. Keep it when you edit the file.
The rest of the options are for larger projects, custom layouts and stricter linting. Each section below gives the default.
Entities#
tags#
The tags that features, global variables and targets may use. Anything else fails lint. Defaults to ['all'], which nothing uses until you tag a definition with it.
environments#
The environments a project's rules, force entries and exposure are grouped by. Leave it out for a project without environments, where features list their rules directly. If present, it needs at least one unique name without surrounding spaces. An empty array fails.
sets#
Set to true to split the project into independent sets, each in its own directory under sets/. Defaults to false.
promotionFlows#
Restricts which promotions are allowed between sets. Each entry has exactly a from and a to:
module.exports = { sets: true, promotionFlows: [ { from: 'dev', to: 'staging' }, { from: 'staging', to: 'production' }, ],}Without it, any set can be promoted to any other.
reservedKeys#
Keys that features, global variables and feature variables may not use. The default protects feature, variation and variable, which generated code and OpenFeature providers rely on. Setting the option replaces the whole list, and an empty array turns the check off. Matching is exact and case sensitive, so checkout.variable does not match variable.
allowFeatureAndVariableKeyCollisions#
By default, a feature and a global variable cannot share a key. Set this to true only when the overlap is intentional.
defaultBucketBy#
The build uses this bucketBy for a feature that leaves it out. Linting still requires bucketBy on every feature, so in practice each feature sets its own. Defaults to 'userId'.
Layout#
namespaceCharacter#
Joins the directories of a namespaced feature, segment or global variable into its key. With the default ., features/checkout/express.yml becomes checkout.express.
Attributes cannot be namespaced, and their names before the configured file extension cannot contain dots. Keep their definition files directly in the configured attributes directory. Dots in object attribute paths such as geo.country are independent of this separator.
Directories#
Each directory can move. Paths may start with <rootDir>, the project's root:
| Option | Default |
|---|---|
attributesDirectoryPath | <rootDir>/attributes |
segmentsDirectoryPath | <rootDir>/segments |
featuresDirectoryPath | <rootDir>/features |
variablesDirectoryPath | <rootDir>/variables |
schemasDirectoryPath | <rootDir>/schemas |
targetsDirectoryPath | <rootDir>/targets |
testsDirectoryPath | <rootDir>/tests |
setsDirectoryPath | <rootDir>/sets |
datafilesDirectoryPath | <rootDir>/datafiles |
stateDirectoryPath | <rootDir>/.featurevisor |
catalogDirectoryPath | <rootDir>/catalog |
stateDirectoryPath holds the state files to commit, and catalogDirectoryPath is where catalog export writes the Catalog.
catalogBasePath#
The URL path the Catalog is served from, such as /flags/. Defaults to /:
module.exports = { catalogBasePath: '/flags/',}It applies to assets, data requests and navigation, and does not change the output directory. Use an absolute path whose parts contain letters, numbers, dots, underscores and hyphens. . and .. on their own are rejected, and a trailing slash is added if missing. For a GitHub Pages project site, use /<repository-name>/ and export with --hash-router. Without it, the host must serve the Catalog's HTML for every path under the prefix.
Build output#
datafileNamePattern#
How each datafile is named, with %s replaced by the target key. Defaults to featurevisor-%s.json.
revisionFileName#
The file in datafiles/ that records the revision. Defaults to REVISION.
prettyState#
Writes state files indented, which keeps their diffs readable in pull requests. Defaults to true.
prettyDatafile#
Writes datafiles indented. Defaults to false, since applications download them.
stringify#
Stores condition expressions in datafiles as strings. Set stringify: false to keep structured expressions instead. This changes their representation, not their meaning, so measure datafile size and evaluation time with your own workload before changing it. Defaults to true.
precomputeMutations#
Decides when global variable mutations are applied. Defaults to true, which resolves override trees without continue: true during the build and stores complete values:
| Override tree | true | false |
|---|---|---|
Mutations, no continue | Build | SDK |
Any continue: true | SDK | SDK |
| Plain values only | Build | Build |
These rows describe ordinary global variables. Composed overrides always emit overrideProgram and run in the SDK, including plain replacements with true. Their base is assembled from evaluated sources, not a static default.
false can shrink datafiles that repeat large values, at the cost of work in the SDK. It does not enable continue or change which override wins, and feature variable mutations always resolve during the build. Older SDKs need upgrading first, as the migration order explains.
Lint rules#
enforceCatchAllRule#
Requires every feature to end with a rule for conditions: '*', in every environment. Defaults to false.
requireOverrideKeysInFeatures#
Requires a key on every variable override in feature rules and variations. Keys show up in detailed evaluations and let promotions protect a single override. Defaults to false.
disallowOperators#
Operators that linting and building reject in attribute and variable conditions and test checks, including nested ones:
module.exports = { disallowOperators: [ 'isNull', 'notNull', 'empty', 'notEmpty', ],}Names are case sensitive. The option restricts what projects may write. It does not change how SDKs evaluate.
allowedConditions#
Limits which kinds of condition each location accepts. A location you leave out accepts every kind, which is the default:
module.exports = { allowedConditions: { rules: ['segment'], segments: ['attribute'], },}Here rules can only name segments, and segments can only compare attributes. Audiences are written once, as segments, and rules reuse them by name.
| Location | Conditions it covers |
|---|---|
segments | A segment's conditions |
features | A feature's root conditions |
rules | Each rule's conditions |
force | Each force entry's conditions |
featureVariableOverrides | Variable overrides in feature rules and variations |
variables | A global variable's root conditions, compositions included |
variableOverrides | Global variable overrides, nested ones included |
| Kind | Condition |
|---|---|
attribute | An attribute comparison, such as { attribute: country, ... } |
segment | A segment key, such as netherlands |
feature | { feature }, with or without variation, and { feature, variable } |
variable | A global variable comparison, such as { variable: maintenanceMode, ... } |
conditions: '*' is always allowed. Conditions inside and, or and not are checked one by one. Lint reports each condition a location does not accept with disallowed_condition, and suggests where it can go instead. Archived definitions are not checked.
Each location is checked on its own. A rule may name a segment that reads a feature, unless segments leaves out feature too.
Building and SDKs ignore this option. Keep the v3 shape gives the configuration matching what v3 allowed.
Variable size limits#
Cap the size of variable values, to keep datafiles small. The limits apply to feature variables and global variables alike, including defaults and overrides. Lint reports a value over the limit with its length and the option's name. All default to no limit:
| Option | Limits |
|---|---|
maxVariableStringLength | Characters in a string |
maxVariableArrayStringifiedLength | Characters of an array as JSON |
maxVariableObjectStringifiedLength | Characters of an object as JSON |
maxVariableJSONStringifiedLength | Characters of a json value |
Extending#
parser#
The format of definition files. Defaults to yml. Set parser: 'json' for JSON, or pass a custom parser.
plugins#
Plugins that add commands to the CLI. Defaults to [].
adapter#
The datasource adapter that reads and writes definitions. Defaults to the filesystem.

