Guides
Core Evaluation Engine
Reuse FlagForge's pure TypeScript engine for deterministic local flag evaluation
Core Evaluation Engine
The @flagforge/core package evaluates feature flags without network access, storage, a clock, or randomness. Give it a per-environment EvaluatableFlag definition and an EvaluationContext, and it returns the selected variation and value.
This is useful when you need FlagForge semantics in a custom Node.js service, worker, edge adapter, or another runtime where the hosted API and SDK are not the right integration. The REST API and @flagforge/sdk use the same engine, so local results follow the same targeting, segment, and rollout rules.
Note:
The core package does not fetch configuration or manage API keys. Obtain or define the flag definitions separately, then pass them to the synchronous evaluation functions.
Install
Install the package in the application that will perform evaluation:
pnpm add @flagforge/coreThe package has no runtime dependencies and exposes its public API from @flagforge/core.
Evaluate one flag
An EvaluatableFlag is a flattened, per-environment definition. It includes the flag type, named variations, enabled state, fallback variation keys, and ordered targeting rules. The context has a stable subject key and optional primitive attributes.
import { evaluateFlag } from "@flagforge/core";
import type { EvaluatableFlag, EvaluationContext } from "@flagforge/core";
const checkoutFlag: EvaluatableFlag = {
key: "new-checkout",
type: "boolean",
enabled: true,
variations: [
{ key: "on", value: true },
{ key: "off", value: false },
],
defaultVariationKey: "off",
offVariationKey: "off",
rules: [
{
id: "pro-users",
conditions: [
{ attribute: "plan", operator: "equals", values: ["pro"] },
],
rollout: {
branches: [
{ variationKey: "on", weight: 25_000 },
{ variationKey: "off", weight: 75_000 },
],
},
},
],
};
const context: EvaluationContext = {
key: "user-123",
attributes: { plan: "pro" },
};
const result = evaluateFlag(checkoutFlag, context);
console.log(result);
// {
// flagKey: "new-checkout",
// value: true, // or false, depending on the deterministic bucket
// variationKey: "on", // or "off"
// reason: { kind: "RULE_MATCH", ruleId: "pro-users", ruleIndex: 0 }
// }evaluateFlag is synchronous. Pass a third argument when the flag uses segment conditions:
import { evaluateFlag } from "@flagforge/core";
import type {
EvaluatableFlag,
EvaluationContext,
Segment,
} from "@flagforge/core";
const betaFlag: EvaluatableFlag = {
key: "beta-dashboard",
type: "boolean",
enabled: true,
variations: [
{ key: "on", value: true },
{ key: "off", value: false },
],
defaultVariationKey: "off",
offVariationKey: "off",
rules: [
{
id: "beta-segment",
conditions: [
{
attribute: "segment",
operator: "inSegment",
values: ["beta-users"],
},
],
variationKey: "on",
},
],
};
const segments: Segment[] = [
{
key: "beta-users",
conditions: [
{ attribute: "beta", operator: "equals", values: [true] },
],
},
];
const context: EvaluationContext = {
key: "user-123",
attributes: { beta: true },
};
const result = evaluateFlag(betaFlag, context, segments);For an inSegment condition, values contains segment keys and attribute is ignored. A missing segment does not match. Segment conditions are combined with logical AND, and nested segment references are cycle-protected.
Evaluate all flags
Use evaluateAll when the same context must be evaluated against a collection of definitions. It returns a map keyed by each flag's key, with one EvaluationResult per flag.
import { evaluateAll } from "@flagforge/core";
import type { EvaluatableFlag, EvaluationContext } from "@flagforge/core";
const flags: EvaluatableFlag[] = [checkoutFlag, {
key: "checkout-label",
type: "string",
enabled: true,
variations: [
{ key: "control", value: "Continue" },
{ key: "new", value: "Start checkout" },
],
defaultVariationKey: "control",
offVariationKey: "control",
rules: [],
}];
const results = evaluateAll(flags, context);
console.log(results["new-checkout"].value);
console.log(results["checkout-label"].variationKey);Provide the same optional segments array as the third argument to evaluateAll when any definition references segments:
const results = evaluateAll(flags, context, segments);Evaluation behavior
The engine resolves each flag in this order:
A disabled flag serves
offVariationKeyand reportsreason.kindasOFF.Enabled flags check rules in array order. Every condition in a rule must match; the first matching rule wins and reports
RULE_MATCHwith itsruleIdand zero-basedruleIndex.If no rule matches, the flag serves
defaultVariationKeyand reportsFALLTHROUGH.
A rule can select a fixed variationKey or a rollout. Rollout weights use a resolution of 100,000, so 25_000 represents 25%. Bucketing hashes the flag key and the selected bucket value. It uses context.key by default; set rollout.bucketBy to use an attribute instead. Keep that identifier stable when you want a subject to remain in the same branch.
Conditions support equals, in, contains, startsWith, endsWith, numeric comparisons, semantic-version comparisons, regular expressions, and inSegment. A condition's values are alternatives, while conditions within a rule or segment are combined with logical AND. Set negate: true to invert a condition.
Note:
Variation and fallback keys must refer to entries in variations. If resolution encounters an unknown variation key, the result uses the off variation when available and reports reason.kind as ERROR with an error message. Keep definitions internally consistent before distributing them to applications.
Read the result safely
EvaluationResult contains four fields:
| Field | Meaning |
|---|---|
flagKey | The evaluated flag's key. |
value | The selected boolean, string, number, or JSON value. |
variationKey | The named variation selected by the engine. An error result uses an empty string. |
reason | A discriminated object explaining why the value was selected. |
Use reason.kind for application diagnostics rather than parsing messages. RULE_MATCH also identifies the matching rule; ERROR includes an error string. A result is still returned for evaluation errors, with a fallback value chosen from the off variation, the first variation, or false when no variation value is available.
When to use the core package
Use @flagforge/core directly when your application already has configuration delivery, needs evaluation in a non-SDK runtime, or wants to share the exact engine semantics with another TypeScript component.
Use @flagforge/sdk when you want FlagForge to bootstrap definitions from GET /v1/config, evaluate them locally, and optionally poll for changes. Use the REST evaluation endpoints when configuration should remain behind the API instead of being copied into the application process.
Bootstrap definitions from FlagForge and use typed getters, polling, and change listeners in a Node.js application.
Review evaluation context fields, targeting order, segment matching, rollout behavior, and result reasons.