@setu-ts/feature-flags-plugin — control feature rollout and route access
Add it: setu add feature-flags adds it and prints the registration for setu.config.ts; add
FeatureFlagsPlugin with a provider. It provides CAPABILITIES.FEATURE_FLAGS (IFeatureFlags).
Define a targeted rollout
import { FeatureFlagsPlugin } from '@setu-ts/feature-flags-plugin';
export const flags = FeatureFlagsPlugin({
provider: 'config',
options: {
flags: {
'new-checkout': { enabled: false, users: ['user-1'] },
'dark-mode': { enabled: true, percentage: 25 },
},
},
});
Guard a route and evaluate a flag
import { CAPABILITIES } from '@setu-ts/common';
import type { IFeatureFlags, IRouterApi } from '@setu-ts/common';
import { createFlagGuard } from '@setu-ts/feature-flags-plugin';
export function registerCheckoutRoutes(router: IRouterApi): void {
router.get('/checkout', {
middleware: [createFlagGuard('new-checkout', { fallback: '/waitlist' })],
handler: (ctx) => {
const flags = ctx.services.get<IFeatureFlags>(CAPABILITIES.FEATURE_FLAGS);
const dark = flags.isEnabled('dark-mode', ctx.request.user ? { userId: ctx.request.user.id } : undefined);
return ctx.response.json({ dark });
},
});
}
Behaviour the types do not show
- For local definitions, unknown flags are false. A non-empty
tenantslist restricts first, then a matchingusersentry overrides evenenabled: false. - Partial percentage rollouts need
userId; their bucket is deterministic for flag plus user.percentage: 100does not require a user id. - The guard derives user and tenant ids from the request unless
contextoverrides them. An enabled flag calls the handler; disabled redirects tofallbackor returns 404 by default. - Missing
CAPABILITIES.FEATURE_FLAGSpropagates the registry error instead of silently allowing. isEnabledis synchronous.isEnabledAsyncawaits an asynchronous provider when available, otherwise resolves the synchronous result.
Other options
provideralso selects'memory','database','launchdarkly'or'custom'.MemoryProvidersupportssetFlag,removeFlagandreplaceFlags; config flags stay fixed.statusCodeoverrides the guard’s refusal status;fallbacktakes precedence.- LaunchDarkly accepts
sdkKeyor injectedclient,fallbackValue,initTimeoutSecondsandldOptionsinoptions.