Flag

Feature-flag primitive shaped to the OpenFeature specification (https://openfeature.dev/specification/). Per-tenant overrides ride on the targeting context ({ targetingKey, environment, ...attrs }); every flip of a flag value at the provider layer audits via flag.evaluated / flag.evaluation.error on the framework audit chain so operators see the change without scraping provider state. Kill-switch semantics fall out of the same resolution chain: a provider that returns value: false for a targeting key disables the gated path immediately — no client restart, no cache flush.

Validation policy: - create() throws on bad opts (provider missing .evaluate(flagKey, ctx), hooks not function-shaped, etc.). - The hot path (getBoolean / getString / getNumber / getObject / getValue) NEVER throws — a provider that errors emits flag.evaluation.error and the call returns the operator-supplied default. A request can't be taken down by a misconfigured flag.

Composability: flag.middleware({ userKey }) attaches a per-request req.flag accessor that bakes the request's targeting context in, so handlers call req.flag.getBoolean(key) without threading context through every site. Multiple providers can be stacked via opts.providers or addProvider/removeProvider — first non-flag_not_found response wins, so a local-file override beats the central provider for break-glass scenarios.

b.flag.create(opts) #

stable0.6.0
{
  provider:                  { evaluate: (flagKey, ctx) => result, kind?: string, list?: () => [string] },
  providers:                 [provider],                    // additional providers (first non-not-found wins)
  defaultEvaluationContext:  { targetingKey?: string, [attr: string]: any },
  audit:                     boolean,                        // emit flag.evaluated / flag.evaluation.error; default true
  errorHandler:              ({ flagKey, err, ctx }) => void,
  hooks:                     { before?: fn, after?: fn, error?: fn, finally?: fn }, // OpenFeature hook stages
}

Build an OpenFeature-shaped flag client backed by one or more providers. The returned object exposes typed getters (getBoolean / getString / getNumber / getObject / getValue), a richer getDetails(flagKey, ctx) (returns { value, variant, reason, metadata }), batch helpers (getValues / getDetailsAll), provider mutation (addProvider / removeProvider), and middleware({ userKey }) for per-request binding.

Throws FlagError at config time on a missing provider, hooks not shaped as { before?, after?, error?, finally? } functions, or a provider lacking .evaluate(flagKey, ctx).

var provider = {
  kind: "memory",
  evaluate: function (flagKey, ctx) {
    if (flagKey === "new-checkout-flow") {
      return { value: ctx.targetingKey === "user-42", variant: "on", reason: "TARGETING_MATCH" };
    }
    return { reason: "flag_not_found" };
  },
};

var flag = b.flag.create({
  provider: provider,
  defaultEvaluationContext: { environment: "production" },
});

flag.getBoolean("new-checkout-flow", { targetingKey: "user-42" });   // → true
flag.getBoolean("new-checkout-flow", { targetingKey: "user-99" });   // → false
flag.getString ("missing-key",       { targetingKey: "u" }, "fallback");   // → "fallback"

// Per-request binding via middleware:
//   app.use(flag.middleware({ userKey: "id" }));
//   // handler: if (req.flag.getBoolean("new-checkout-flow")) { ... }

Last updated 2026-08-08T16:39:49.652Z by seeder.