API reference

flags

Every export of @kitr/flags, @kitr/flags/react, @kitr/flags/provider, @kitr/flags/server, read from the published type definitions. The guide shows them in use.

@kitr/flags

createFlags

function createFlags<D extends FlagDefinitions>(definitions: D, config?: FlagsConfig, app?: KitrApp, options?: CreateFlagsOptions): Flags<D>;

Build a flags service from definitions and a config: what the provider binds, and what tests call directly.

defineFlags

function defineFlags<const D extends FlagDefinitions>(definitions: D): D;

Declare every flag once, in app/flags.ts, with its default and a sentence on what it gates.

envFlagsProvider

function envFlagsProvider(env?: Record<string, string | undefined>, prefix?: string): FlagsProvider;

Answer from environment variables: FLAG_NEW_DASHBOARD=true answers newDashboard and new-dashboard. Reads process.env unless given a map.

openFeatureFlagsProvider

function openFeatureFlagsProvider(client: {
  getBooleanValue(key: string, def: boolean, ctx?: any): boolean | Promise<boolean>;
  getStringValue(key: string, def: string, ctx?: any): string | Promise<string>;
  getNumberValue(key: string, def: number, ctx?: any): number | Promise<number>;
}, definitions: FlagDefinitions): FlagsProvider;

An OpenFeature client (@openfeature/web-sdk or server-sdk) as a kitr provider; the typed defaults in app/flags.ts pick which get*Value to call.

percentageFlagsProvider

function percentageFlagsProvider(shares: Record<string, number>, salt?: string): FlagsProvider;

Turn a flag on for a share of users, stable per user id. percentageFlagsProvider({ beta: 10 }).

readEmbeddedFlags

function readEmbeddedFlags(): FlagMap | undefined;

Read the snapshot a server page embedded, if any. undefined on the server, without the tag, or when its JSON is broken.

staticFlagsProvider

function staticFlagsProvider(values: Partial<FlagMap> | ((context: FlagContext) => Partial<FlagMap>)): FlagsProvider;

A fixed map, or a function of the context: { newDashboard: true } or (ctx) => ....

useFlags

function useFlags<D extends FlagDefinitions = FlagDefinitions>(app: KitrApp): Flags<D>;

The app's flags service, from the container, outside React.

defineConfig

const defineConfig: <const C extends FlagsConfig>(config: C) => C;

Type the default export of config/flags.ts. Returns its argument unchanged.

FLAGS_SCRIPT_ID

const FLAGS_SCRIPT_ID = "__kitr_flags__";

The id of the JSON script tag that carries the server's snapshot to the browser.

CreateFlagsOptions

interface CreateFlagsOptions {
  /**
   * Whether `resolve()` updates the snapshot that `value()`, `isEnabled()` and the React hooks
   * read. Default: everywhere but the server's root app, which serves every user at once (the
   * provider binds one instance per request there, and those keep their snapshot).
   */
  keepSnapshot?: boolean;
}

Options for createFlags() beyond the config.

FlagContext

interface FlagContext {
  /** Stable id of the user; percentage rollouts and OpenFeature's `targetingKey` use it. */
  userId?: string;
  [key: string]: unknown;
}

Who is asking: the user, their plan, the request. Providers target on it.

FlagDefinition

interface FlagDefinition<T extends FlagValue = FlagValue> {
  /** The value when no provider answers. */
  default: T;
  /** A sentence on what the flag gates, for the people who flip it. */
  description?: string;
}

One flag in defineFlags(): its default, whose type fixes the flag's type, and what it gates.

Flags

interface Flags<D extends FlagDefinitions = FlagDefinitions> {
  /** The current answer. Synchronous: works from a resolved snapshot (and the defaults before one). */
  value<K extends keyof D & string>(key: K): D[K]['default'];
  /** True only when the flag's current value is exactly `true`. */
  isEnabled(key: keyof D & string): boolean;
  /** Every flag, resolved for `context`. Server: once per request. Client: once per session, then cached. */
  resolve(context?: FlagContext): Promise<FlagValues<D>>;
  /** Load a snapshot the server resolved (what `<FlagsScript>` carries), skipping any resolution. */
  hydrate(values: Partial<FlagValues<D>>): void;
  /** Every current value, as one frozen object that stays the same until the next change. */
  snapshot(): FlagValues<D>;
  /** Call `listener` after each resolve or hydrate that updates the snapshot; returns the unsubscribe. */
  subscribe(listener: () => void): () => void;
  /** The definitions passed in. */
  readonly definitions: D;
  /** Whether the snapshot came from a resolve or hydrate rather than the defaults. */
  readonly resolved: boolean;
}

The flags service bound as flags: a typed snapshot of every flag, and the means to resolve it.

FlagsConfig

interface FlagsConfig {
  /** The flags from `app/flags.ts`. */
  definitions?: FlagDefinitions;
  /** First provider with an answer wins. */
  providers?: FlagsProvider[];
  /**
   * Seconds a resolved snapshot stays fresh in the browser. After that the client resolves again,
   * with the context of its last `resolve()`, so a flag turned off reaches open tabs. Default: never.
   */
  ttl?: number;
}

The options in config/flags.ts.

FlagsProvider

interface FlagsProvider {
  /** Shown in warnings when the provider fails. */
  name: string;
  /** Runs once per `resolve()`, before any key. A throw skips this provider for that resolve. */
  init?(context: FlagContext): Promise<void> | void;
  /** `undefined` means "no opinion": the next provider, then the default, answer. */
  resolve(key: string, context: FlagContext): Promise<FlagValue | undefined> | FlagValue | undefined;
}

Where answers come from. Mirrors OpenFeature's provider shape, so an OpenFeature client is a one-line adapter, but a plain object or envFlagsProvider() also work.

FlagDefinitions

type FlagDefinitions = Record<string, FlagDefinition>;

Every flag by key, as defineFlags() takes and returns them.

FlagMap

type FlagMap = Record<string, FlagValue>;

Flag values by key, untyped: what providers and embedded snapshots carry.

FlagValue

type FlagValue = boolean | string | number;

A flag's value: on/off, a number such as a limit, or a string such as a variant name.

FlagValues

type FlagValues<D extends FlagDefinitions> = {
  [K in keyof D]: D[K]['default'];
};

The resolved value of every flag in D, typed by each default.

@kitr/flags/react

FlagsScript

function FlagsScript({ values }: {
  values: Record<string, unknown>;
}): import("react").DetailedReactHTMLElement<{
  type: string;
  id: string;
  dangerouslySetInnerHTML: {
    __html: string;
  };
}, HTMLElement>;

Put in the document head of the root route with the values resolveFlags() returned in the loader.

useFlag

function useFlag<D extends FlagDefinitions, K extends keyof D & string>(key: K): D[K]['default'];

One flag's value; re-renders when a resolve or hydrate changes it.

useFlags

function useFlags<D extends FlagDefinitions = FlagDefinitions>(): FlagValues<D>;

Every flag, as a plain object.

@kitr/flags/provider

flagsProvider

const flagsProvider: import("@kitr/core").KitrProvider;

Binds flags from config/flags.ts (definitions from app/flags.ts, providers). In the browser a snapshot embedded by <FlagsScript> loads before the first render; otherwise resolve() runs at boot. On the server, resolveFlags(app, event) answers per request.

@kitr/flags/server

flagsScript

function flagsScript(values: Record<string, unknown>): string;

The server-resolved snapshot as a script tag the client provider reads before hydration.

resolveFlags

function resolveFlags<D extends FlagDefinitions>(app: KitrApp, event: H3Event | {
  context: Record<string, any>;
}, context?: FlagContext): Promise<FlagValues<D>>;

Resolve every flag for this request (user from event.context.kitr.locals.user when auth ran), cached on the event so loaders, controllers and the page share one answer.

Flags

interface Flags<D extends FlagDefinitions = FlagDefinitions> {
  /** The current answer. Synchronous: works from a resolved snapshot (and the defaults before one). */
  value<K extends keyof D & string>(key: K): D[K]['default'];
  /** True only when the flag's current value is exactly `true`. */
  isEnabled(key: keyof D & string): boolean;
  /** Every flag, resolved for `context`. Server: once per request. Client: once per session, then cached. */
  resolve(context?: FlagContext): Promise<FlagValues<D>>;
  /** Load a snapshot the server resolved (what `<FlagsScript>` carries), skipping any resolution. */
  hydrate(values: Partial<FlagValues<D>>): void;
  /** Every current value, as one frozen object that stays the same until the next change. */
  snapshot(): FlagValues<D>;
  /** Call `listener` after each resolve or hydrate that updates the snapshot; returns the unsubscribe. */
  subscribe(listener: () => void): () => void;
  /** The definitions passed in. */
  readonly definitions: D;
  /** Whether the snapshot came from a resolve or hydrate rather than the defaults. */
  readonly resolved: boolean;
}

The flags service bound as flags: a typed snapshot of every flag, and the means to resolve it.