← all parts

Part · flags.feature

What's actually behind flags.feature

The part exactly as partkit add flags.feature vendors it into your repo — verified, locked, every byte readable. Nothing here is mocked.

flags.featurev1.1.0

✓ attested🔒 read-onlyflags.feature@1

Lives at parts/flags.feature/ in your repo — open, owned, readable. Not buried in node_modules. 578 lines of source you can audit.

15 conformance tests passedverified 2026-06-15↗ CI run
content hash 838f5ec9ed…d35901pinned in parts.lockctrlai guard fails CI if a single byte changes
tested against node 25.3.0

Public API — what your seam calls

  • flags(db: SqlExecutor): FlagSet
  • FlagSet { evaluate<T extends FlagValue>(key: string, context: EvalContext, fallback: T): Promise<T>; evaluateAll(context: EvalContext): Promise<Record<string, FlagValue>>; setFlag(def: FlagDefinitionInput): Promise<void>; getFlag(key: string): Promise<FlagDefinition | null>; listFlags(): Promise<FlagDefinition[]>; archiveFlag(key: string): Promise<void> }
  • class FlagError extends Error { code: FlagErrorCode }
  • types: SqlExecutor, FlagValue, FlagType, Json, EvalContext, Condition, Rule, Variant, FlagDefinition, FlagDefinitionInput, FlagSet, FlagErrorCode

Invariants — guarantees the contract pins

  1. Importing the part performs no I/O and never throws; management operations validate input with a typed FlagError, and evaluate/evaluateAll are FAIL-SAFE — an unknown flag, a stored value whose type differs from the flag's declared type, or a storage error returns the caller's fallback (or omits the flag) and never throws
  2. Evaluation is deterministic and sticky: the same (key, subjectId) resolves to the same variant for a given flag config; percentage rollout buckets uniformly by a stable hash of the flag key and subject id, so a subject does not flicker between requests and a 10% rollout is ~10% of subjects
  3. Targeting rules are first-match-wins, with each rule's conditions AND-ed against the context attributes; a non-matching or attribute-less context falls through to rollout, then to the flag's default
  4. A disabled flag resolves to its configured default; an archived or unknown flag resolves to the caller's fallback
  5. Flag values are type-checked against the flag's declared type — a value of the wrong type never escapes as the wrong type (it fails safe to the fallback); setFlag rejects a default, variant, or rule value that does not match the declared type
  6. The part operates solely through the provided SqlExecutor seam — it imports no database driver — and every statement targets only its own feature_flags table; every input is parameterized, so SQL metacharacters are stored literally and never executed

Owns in your Postgres

feature_flags

Dependencies

zero-dep — runs on your Postgres
SOURCEparts/flags.feature/15 files · click to read
parts/flags.feature/src/index.tstypescript · 1,378 bytes
/**
 * flags.feature — public interface. The ONLY legal import surface.
 * Contract: ../contract.json · What your app must provide: ../seams.md
 *
 * Typed feature flags with targeting rules and sticky percentage rollout, on a
 * FAIL-SAFE evaluation hot path: a flag outage never throws — it returns your
 * fallback. Bind it to a database connection (the SqlExecutor seam); constructing
 * it performs no I/O.
 */
import { createFlagSet } from "./internal/flags";
import type { FlagSet, SqlExecutor } from "./internal/types";

export { FlagError } from "./internal/errors";
export type { FlagErrorCode } from "./internal/errors";
export type {
  Condition,
  ConditionOp,
  EvalContext,
  FlagDefinition,
  FlagDefinitionInput,
  FlagSet,
  FlagType,
  FlagValue,
  Json,
  Rule,
  SqlExecutor,
  Variant,
} from "./internal/types";

/**
 * Bind the flag set to a database connection (the SqlExecutor seam).
 * Constructing it performs no I/O and never throws (contract invariant 1).
 *
 *   const ff = flags(db);
 *   if (await ff.evaluate("new-checkout", { subjectId: user.id }, false)) { … }
 *   await ff.setFlag({ key: "new-checkout", type: "boolean", enabled: true,
 *                      default: false, rollout: [{ value: true, weight: 10 }, { value: false, weight: 90 }] });
 */
export function flags(db: SqlExecutor): FlagSet {
  return createFlagSet(db);
}