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.