Skip to Content
FeatureOverview

@evanion/feature

@evanion/feature 0.1.0 and its patch releases up to 0.1.1, the version on npm. Values on this page were produced by running the source at e53b2d8, a later commit than the 0.1.0 tag, and are not re-executed.

Define your feature flags in a plain document you own and evaluate them across your Node.js API, build scripts, and the browser using a single library.

Imagine a game shop where a static build renders a page for every game. Flags that are on for everyone are written directly into the HTML during this build. However, a new checkout is rolled out to only 25% of customers; since the build cannot know who will visit, it leaves that decision for the request and records what information the request must provide to resolve it. In the API, these flags are then decided on a per-request basis.

By using one library across the build script, the Node.js API, and the browser, you ensure a single source of truth without introducing framework dependencies into your core logic. This closes the gap between where flags are defined and where they are executed.

Dependency-Aware Resolution

When every flag is treated as an independent switch, a critical failure occurs: a child feature, such as express pickup, can be enabled for a customer while the parent it requires, the new checkout, remains off, resulting in a broken page.

To prevent these experiences, teams often attempt to write manual checks in every component or manage state manually by updating every child flag whenever a parent changes. Both approaches fail because they rely on perfect consistency; any component that forgets the parent check ships a broken page.

@evanion/feature solves this by moving the dependency logic into a hierarchical decision engine. A feature resolves off whenever a feature it depends on resolves off, checking the entire dependency chain transitively with no manual updates to child flags.

import { } from '@evanion/feature'; const = ([ { : 'new-checkout', : true, : [ { : 'booksellers-first', : [{ : 'role', : 'eq', : 'bookseller' }], }, ], }, { : 'express-pickup', : true, : ['new-checkout'], }, ]); // A bookseller gets the new checkout AND the express pickup const = { : 'bookseller' }; .('express-pickup', ); // -> true // A customer is denied the checkout, so they are automatically denied pickup const = { : 'customer' }; .('express-pickup', ); // -> false

In @evanion/feature, a feature’s definition holds its intent, enabled, and its rules. A rule matches fields of the context you pass, such as a customer’s role. A feature resolves on when it is enabled, every feature in its dependsOn resolves on, and one of its rules matches or it has none.

In the example above, express pickup is offered only to customers who are already on the new checkout. The new checkout’s one rule matches a bookseller and nobody else, so for a customer it resolves to false, and express pickup resolves to false with it, even though its own definition has no rules and would resolve on alone.

The Decision Record

To provide more than a simple boolean, @evanion/feature returns a decision object for every feature when you call resolve():

  • enabled: The final answer (true/false).
  • reason: A code naming why the feature is on or off: default-on, rule-match, explicitly-off, no-rule-matched or dependency-off.
  • blockedBy: On a dependency-off decision, the parent that blocked it.

The reason field allows a developer to debug a flag. If a feature is unexpectedly off because of a parent, its reason is dependency-off and blockedBy names the parent to look at. Nothing in the library reads reason back, so it explains a decision and never changes one.

The Core and the React Package

@evanion/feature imports no framework. It runs in a Node.js API, a build script or a serverless function, and it resolves flags and plans which of them a build can settle. Every page in this section is about that package.

@evanion/react-feature is the React binding, and it is a package of its own because 'use client' is a per-module directive. It carries a FeatureProvider and four hooks that read its decisions anywhere in a React tree, and it decides nothing. The react-feature pages are where it is documented.

The Store Never Writes

Resolving in @evanion/feature is a pure function of the configuration, the context you pass and the current time. The only writer is toggle(), which sets a feature’s enabled intent.

The engine never writes resolved values back into the configuration. This ensures that your configuration remains a record of intent rather than a log of results, and that multiple processes resolving the same context against the same configuration will always agree.

What It Leaves to You

@evanion/feature evaluates flags in your process and stops there:

  • It fetches no configuration. createFeatures takes the definitions your application loaded.
  • It stores no variant assignment. A context can hand back one your application saved.
  • It records no exposure. A component records it where it renders.
  • It declares no drain(). An observer’s own transport flushes what it buffered.
  • What it does not do lists all nine boundaries and the file that fixes each.

What It Covers

The following guides detail the capabilities of @evanion/feature:

You now know how @evanion/feature uses a single library to evaluate a team-owned document across environments and why it uses a dependency-aware engine. Getting Started installs @evanion/feature and resolves the new checkout and express pickup for a customer.

Last updated on