Skip to content

Repository files navigation

@lovart-open/flags

CInpm version

Type-safe Feature Flag and Parameter Store library built on Statsig, with a fully synchronous architecture.

Installation

npm install @lovart-open/flags
# or
pnpm add @lovart-open/flags

Initialize Statsig

Initialize the Statsig client at your app entry point:

import{initStatsigClient}from'@lovart-open/flags/statsig';initStatsigClient('your-statsig-client-key',{userID: 'user-123'},{environment: {tier: 'production'},});

Feature Flags

Core Features

  • Type-safe: Full TypeScript type inference and autocomplete
  • Synchronous: No loading states, no skeleton screens
  • Multi-layer priority: URL > testOverride > override > remote > fallback(false)

Define and Create

import{createFlagStore,typeFlagDefinition}from'@lovart-open/flags/statsig';// 1. Define your flagsconstMY_FLAGS={dark_mode: {description: 'Dark mode toggle'},new_checkout: {description: 'New checkout flow',testOverride: true,// Force enable in E2E tests},beta_feature: {description: 'Beta feature',override: false,// Static override, ignores remote},}asconstsatisfiesRecord<string,FlagDefinition>;// 2. Create type-safe store and hooksexportconst{ flagStore, useFlag, useFlagState }=createFlagStore(MY_FLAGS);// 3. Export types (optional)exporttypeMyFlagKey=keyoftypeofMY_FLAGS;

React Usage

import{useFlag,useFlagState}from'./my-flags';functionApp(){// Get boolean value directlyconstisDark=useFlag('dark_mode');// ✓ autocomplete// Get full state with source infoconststate=useFlagState('new_checkout');console.log(state.flag,state.source);// true, 'remote'returnisDark ? <DarkTheme/> : <LightTheme/>;}

Non-React Usage

import{flagStore}from'./my-flags';// Get single flagconstenabled=flagStore.getFlag('dark_mode');// Get snapshot of all flagsconstsnapshot=flagStore.snapshot;

URL Override for Debugging

?ff.dark_mode=1 → Force enable
?ff.dark_mode=0 → Force disable

Parameter Store

Core Features

  • Type-safe: Zod schema validation + TypeScript inference
  • Synchronous: Same as Feature Flags
  • Multi-layer priority: URL > testOverride > override > remote > fallback

Define and Create

import{z}from'zod';import{createParamStore,defineParam,typeParamStoreDefinition}from'@lovart-open/flags/statsig';// 1. Define your param storesconstMY_PARAMS={homepage_cta: {description: 'Homepage CTA button',params: {text: defineParam({schema: z.enum(['Learn More','Get Started','Sign Up']),fallback: 'Learn More',description: 'Button text',}),color: defineParam({schema: z.enum(['gray','red','blue']),fallback: 'gray',testOverride: 'blue',// Use in E2E tests}),visible: defineParam({schema: z.boolean(),fallback: true,}),},},pricing: {description: 'Pricing config',params: {discount: defineParam({schema: z.number().min(0).max(100),fallback: 0,}),currency: defineParam({schema: z.enum(['USD','CNY','EUR']),fallback: 'USD',}),},},}asconstsatisfiesRecord<string,ParamStoreDefinition<any>>;// 2. Create type-safe store and hooksexportconst{ paramStore, useParam, useParamState, useParamStore }=createParamStore(MY_PARAMS);

React Usage

import{useParam,useParamStore}from'./my-params';functionCTAButton(){// Get value directly (with full type hints)consttext=useParam('homepage_cta','text');// 'Learn More' | 'Get Started' | 'Sign Up'constcolor=useParam('homepage_cta','color');// 'gray' | 'red' | 'blue'// Or get entire store handleconststore=useParamStore('homepage_cta');constvisible=store.get('visible');// booleanif(!visible)returnnull;return<buttonstyle={{ color }}>{text}</button>;}

Non-React Usage

import{paramStore}from'./my-params';// Get single paramconstdiscount=paramStore.getParam('pricing','discount');// number// Get store handleconststore=paramStore.getStore('pricing');store.get('currency');// 'USD' | 'CNY' | 'EUR'

URL Override for Debugging

# Single param override
?fp.homepage_cta.text=Get Started
?fp.pricing.discount=20
# Entire store JSON override
?fp.homepage_cta={"text":"Get Started","visible":false}

Advanced Configuration

Custom Logger

import{initStatsigClient,setLogger}from'@lovart-open/flags/statsig';// Option 1: Pass during initinitStatsigClient('client-xxx',{userID: 'user-123'},{logger: (message)=>myLogger.info(message),});// Option 2: Set globallysetLogger((message)=>myLogger.info(message));

E2E Test Support

Configure isTestEnv in initialization to enable testOverride values:

initStatsigClient('client-xxx',{userID: 'user-123'},{isTestEnv: ()=>Boolean(window.__E2E__),});// playwright/cypress testsawaitpage.addInitScript(()=>{window.__E2E__=true;});

Server-Side Bootstrap (Zero-Network Init)

initStatsigClient('client-xxx',{userID: 'user-123'},{bootstrap: {data: bootstrapDataFromServer,// Pre-fetched from BFF},});

FlagDefinition Options

PropertyTypeDescription
descriptionstringHuman-readable description
testOverridebooleanFixed value in E2E tests
overridebooleanStatic override (priority over remote)
keepbooleanMark as kept locally (no remote needed)

ParamDefinition Options

PropertyTypeDescription
schemaz.ZodTypeZod schema (required)
fallbackTDefault value (required)
descriptionstringHuman-readable description
testOverrideTFixed value in E2E tests
overrideTStatic override

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages