A tiny, framework‑agnostic TypeScript library for feature flags, with support for simple boolean “allow” flags and integer range checks (exact value, min, max). Bundled with microbundle to ship ESM, CJS, and UMD, plus type definitions.
npm i @sparkstone/feature-flags
# or
pnpm add @sparkstone/feature-flags
# or
yarn add @sparkstone/feature-flagsThe package exports a small API under the featureFlags namespace, plus the FeatureFlag type for authoring flags.
import { featureFlags, FeatureFlag } from "@sparkstone/feature-flags";
// 1) Define your flags (could come from a file, API, or env)
const flags: FeatureFlag[] = [
{ name: "newDashboard", tier: "beta", type: "allow", value: true },
{ name: "maxUploads", tier: "", type: "int:max", value: 10 },
{ name: "minAge", tier: "eu", type: "int:min", value: 16 },
{ name: "build", tier: "qa", type: "int", value: 1234 },
];
// 2) Load them into the in‑memory store
featureFlags.load(flags);
// 3) Check boolean "allow" flags
if (featureFlags.isAllowed("newDashboard", "beta")) {
// show experimental UI
}
// 4) Check integer constraints / ranges
const uploadsToday = 7;
if (featureFlags.isInRange("maxUploads", "", uploadsToday)) {
// within allowed cap
}
// 5) Read back a flag (typed) or iterate them all
const buildFlag = featureFlags.get("build", "qa"); // -> { type: "int", value: 1234, ... }
for (const [key, flag] of featureFlags.getAll()) {
// key = "name.tier", value = FeatureFlag
}Tiers: Every flag has a
tierstring (can be empty). Internally the key is\"\" + name + "." + tier, soname+tierpairs must be unique. Use tiers to segment by environment (e.g.,dev,qa,prod), cohort (beta,control), region (eu,us), plan (free,pro), etc.
All functions live on the featureFlags namespace.
Bulk‑loads an array of flags into the internal map (replaces existing entries with the same name + tier).
Empties the internal map.
Returns a single flag by name + tier or undefined if not found. Logs a warning if missing.
Returns the underlying Map, where the key is "{name}.{tier}" and the value is the FeatureFlag object.
Looks up a flag and returns true iff it exists and has type: "allow" with a truthy value. If the flag exists but is not of type: "allow", a warning is logged and the result is false.
Looks up a flag and, when it is an integer‑type flag, compares the provided value:
{ type: "int", value: N }→value === N{ type: "int:max", value: N }→value <= N(ifvalueisnull, treated as unbounded and returnstrue){ type: "int:min", value: N }→value >= N(ifvalueisnull, treated as unbounded and returnstrue)
If the flag exists but is not an integer‑type, a warning is logged and the result is false.
type Tier = string; // free‑form segment (env, cohort, region, plan, etc.)
interface FeatureFlagBase {
name: string;
tier: Tier; // may be ""
}
type FeatureFlag =
| { type: "allow"; value: boolean }
| { type: "int"; value: number }
| { type: "int:min"; value: number } // lower bound (inclusive)
| { type: "int:max"; value: number } // upper bound (inclusive);The library uses an internal
Map<string, FeatureFlag>to store flags. The key format is"{name}.{tier}".
A UMD build is published under dist/main.umd.js for drop‑in usage without a bundler.
<script src="https://unpkg.com/@sparkstone/feature-flags/dist/main.umd.js"></script>
<script>
const { featureFlags } = window.SparkstoneFeatureFlags;
featureFlags.load([{ name: "newDashboard", tier: "beta", type: "allow", value: true }]);
console.log(featureFlags.isAllowed("newDashboard", "beta"));
</script>The package exposes multiple entry points via package.json exports for modern bundlers and Node.
- ESM: default export path →
dist/main.modern.js - CJS:
requirepath →dist/main.cjs - Types:
dist/main.d.ts
Microbundle also outputs dist/main.module.js and dist/main.umd.js for wider tooling compatibility.
pnpm i
pnpm run dev # watch & rebuild on changes
pnpm run build # create ESM, CJS, UMD + .d.ts in dist/- Keep
namestable across tiers; usetierto target a segment. - Co‑locate flag declarations near the boundary where they’re sourced (e.g., bootstrap file, config module, API payload).
- Prefer boolean flags for on/off behavior and int flags for thresholds/limits; you can always layer your own higher‑level helpers on top of
isAllowed/isInRange. - If you need dynamic/remote flags, load them at startup and re‑
loadon refresh (you may wrap the module with your own cache or reactive layer).
ISC © Sparkstone LLC