Skip to content

API

@ogygia/flags

Feature flags and A/B tests, with flag(), decide() and the exposure stream.

On this page

This page is generated from the package’s own .d.ts files on every build, so the signatures, descriptions and examples below always match the source.

import { … } from '@ogygia/flags';

Exports decide · flag · flush_exposures · prime_flags · BoolFlag · ComponentPick · DecideOptions · ExposureEvent · FlagOptions · FlagQuery · VariantFlag · CtxLike · FlagShape · FlagSource · Resolved

decide

function

Configure flags for this server, once (hooks.server.ts / any server module). Everything is optional — with no decide() at all, flags are native, sticky, and zero-JS.

decide({ source: openfeature(client), overrides: (c) => !!c.visitor?.qa, exposure: send });

function decide(opts: DecideOptions): void;

flag

function

function flag<V extends string, Value = unknown>(name: string, shape: Record<V, number>, opts?: FlagOptions<Value>): VariantFlag<V, Value>;
function flag<Value = unknown>(name: string, shape?: number | ((c: CtxLike) => boolean | undefined), opts?: FlagOptions<Value>): BoolFlag<Value>;

flush_exposures

function

Drain queued exposures to the sink. Called at each request's end (serverless-safe tail) and on the size/age caps. A sink failure is contained — a lost batch is never a request's problem.

function flush_exposures(): void;

prime_flags

function

Resolve the source ONCE per request over every registered flag, caching decisions on the Request so reads stay sync. Awaited by the flags plugin's request stage (flags/server.ts), above the page / hole split. Idempotent; a no-op without a source. Never throws — a source failure degrades every flag to its native rule, it does not take the request down.

function prime_flags(c: CtxLike): Promise<void>;

BoolFlag

interface

A boolean flag — calling it answers "does this request get the feature?".

interface BoolFlag<Value = unknown> extends FlagBase<Value> {/*…*/}

BoolFlag.variants

readonly variants: readonly ("off" | "on")[];

BoolFlag.pick

Page slot (no c — the router supplies it): page(flag.pick({ … })). Pick this visitor's entry from a per-variant map ('off' | 'on'), typed + total.

pick(map: Record<"off" | "on", unknown>): ComponentPick;

BoolFlag.pick

Page slot (no c — the router supplies it): page(flag.pick({ … })). Pick this visitor's entry from a per-variant map ('off' | 'on'), typed + total.

pick<T>(c: CtxLike, map: Record<"off" | "on", T>): T;

ComponentPick

interface

A per-request component chooser for page() — page(hero.pick({ control: A, bold: B })). Branded (not a bare function) because Svelte 5 components ARE functions.

interface ComponentPick {/*…*/}

DecideOptions

interface

interface DecideOptions {/*…*/}

DecideOptions.source

Where decisions come from (a vendor). Omit for pure-native flags (still sticky + zero-JS).

source?: FlagSource;

DecideOptions.overrides

Honor ?og-exp=name:variant beyond dev — gate on something a visitor can't forge (a session role, a signed QA cookie). Default: dev only.

overrides?: (c: CtxLike) => boolean;

DecideOptions.exposure

Exposure sink — called with a BATCH of events (batching is built in: flushed at max events, after ms, or at each request's end). A throwing sink never breaks a request.

exposure?: (events: ExposureEvent[]) => void | Promise<void>;

DecideOptions.batch

Exposure batch size / age caps. Defaults: max 25, ms 2000.

batch?: {
    max?: number;
    ms?: number;
  };

ExposureEvent

interface

interface ExposureEvent {/*…*/}

ExposureEvent.name

name: string;

ExposureEvent.variant

variant: string;

ExposureEvent.sub

the sticky identity the decision keyed on (undefined = anonymous)

sub?: string;

ExposureEvent.at

at: number;

FlagOptions

interface

Rare knobs (third arg to flag).

interface FlagOptions<Value = unknown> {/*…*/}

FlagOptions.layer

Mutual-exclusion group. Members sharing a layer string split visitors into equal LANES: one sticky hash of (group, visitor) over the sorted member names picks each visitor's OWNER, and every other member answers control before its own rule runs — two experiments on one surface never share a visitor, so their measurements stay attributable. Real exposure = a member's split ÷ member count. Override/carried/source outrank the gate.

layer?: string;

FlagOptions.value

Standard Schema for a vendor-authored payload read via .value(c). A vendor value that fails the schema is IGNORED (never enters) — the same trust-boundary law as body schemas.

value?: StandardSchemaV1<Value>;

FlagOptions.fallback

Required alongside value: the payload when the source is silent or a value is invalid, so .value(c) never returns undefined.

fallback?: Value;

FlagQuery

interface

What the source is asked to decide — the app's flags for this request.

interface FlagQuery {/*…*/}

FlagQuery.name

name: string;

FlagQuery.variants

variants: readonly string[];

FlagQuery.kind

'boolean' for a kill switch / rollout / targeting flag, 'variant' for a weighted set.

kind: "boolean" | "variant";

VariantFlag

interface

A weighted-variant flag — calling it answers which variant this request gets.

interface VariantFlag<V extends string, Value = unknown> extends FlagBase<Value> {/*…*/}

VariantFlag.variants

readonly variants: readonly V[];

VariantFlag.pick

Page slot (no c — the router supplies it): page(flag.pick({ … })). Pick this visitor's entry from a per-variant map, typed + total.

pick(map: Record<V, unknown>): ComponentPick;

VariantFlag.pick

Page slot (no c — the router supplies it): page(flag.pick({ … })). Pick this visitor's entry from a per-variant map, typed + total.

pick<T>(c: CtxLike, map: Record<V, T>): T;

CtxLike

type

type CtxLike = {
  request: Request;
  url: URL;
  cookies?: {
    get(name: string): string | undefined;
  };
  visitor?: (Record<string, unknown> & {
    sub?: string;
  }) | undefined;
};

FlagShape

type

The shape a flag is declared with (second arg):

  • omitted → a kill switch (off until decided on),
  • a number → sticky rollout percent (0–100) of the feature,
  • a function → per-request targeting (true/false, or undefined to fall through),
  • a record → weighted variants (ratios; the first key is control).
type FlagShape<V extends string> = undefined | number | ((c: CtxLike) => boolean | undefined) | Record<V, number>;

FlagSource

type

A decision source — OpenFeature, OFREP, or any function. Given the app's flags + the request context, return each flag's decision (missing keys fall through to the native rule). Resolved ONCE per request (awaited by handle() / the router), so every read stays sync.

type FlagSource = (queries: FlagQuery[], c: CtxLike) => Record<string, Resolved> | Promise<Record<string, Resolved>>;

Resolved

type

One decided flag from the SOURCE: a variant name (validated against the flag's declared variants), optionally with a vendor-authored value (validated against the flag's value schema). A bare boolean/string is accepted too — sugar for { variant }.

type Resolved = {
  variant: string;
  value?: unknown;
} | string | boolean;