Skip to main content

Feature Management

The feature-management module provides a vendor-agnostic service for evaluating feature flags and retrieving dynamic configurations. It ships built-in adapters for Unleash and Statsig, and a mockProvider for unit testing.

Import

import {
featureManagement,
unleashProvider,
statsigProvider,
mockProvider,
} from '@open-kerno/commons/feature-management';

featureManagement(provider)

An async factory that initializes the given provider and returns a ready-to-use service instance. If initialization fails, the service stays functional and returns safe defaults for all calls until the underlying SDK recovers.

Signature

async function featureManagement(provider: FeatureProvider): Promise<FeatureManagementService>

Parameters

ParameterTypeDescription
providerFeatureProviderAn initialized provider adapter (Unleash, Statsig, Mock, or custom)

Returns

A FeatureManagementService object:

interface FeatureManagementService {
areEnabled(params: AreEnabledParams): boolean[];
isEnabled(params: IsEnabledParams): boolean;
getConfig<T>(params: GetServiceConfigParams<T>): T | undefined;
shutdown(): Promise<void>;
}

interface AreEnabledParams {
flags: string[];
context?: FeatureContext;
}

interface IsEnabledParams {
flag: string;
context?: FeatureContext;
}

interface GetServiceConfigParams<T> {
key: string;
context?: FeatureContext;
fallback?: T;
}

Example

import { featureManagement, unleashProvider } from '@open-kerno/commons/feature-management';

const svc = await featureManagement(
unleashProvider({
url: 'https://unleash.example.com/api',
token: process.env.UNLEASH_TOKEN,
appName: 'my-service',
}),
);

if (svc.isEnabled({ flag: 'new-checkout' })) {
// run new checkout flow
}

await svc.shutdown();

FeatureContext

A standardized context object passed to flag and config evaluations. Each provider adapter maps it to its SDK-specific format.

interface FeatureContext {
userId?: string;
sessionId?: string;
ip?: string;
environment?: string;
properties?: Record<string, string>;
}

Methods

svc.isEnabled(params)

Evaluates whether a feature flag is enabled. Always synchronous — evaluated against the locally cached ruleset downloaded during initialization. Returns false on any error or if the flag does not exist.

const enabled = svc.isEnabled({ flag: 'dark-mode', context: { userId: 'u-123' } });

svc.areEnabled(params)

Evaluates multiple feature flags in a single call. Returns a boolean[] in the same order as the input flags array. Each entry follows the same fail-safe semantics as isEnabled — returns false on error or if the flag does not exist.

const [darkMode, betaCheckout, newNav] = svc.areEnabled({
flags: ['dark-mode', 'beta-checkout', 'new-nav'],
context: { userId: 'u-123' },
});

svc.getConfig<T>(params)

Retrieves a dynamic configuration value. Returns fallback (or undefined) if the key does not exist, the provider is not ready, or parsing fails.

interface CheckoutConfig {
maxItems: number;
currency: string;
}

const config = svc.getConfig<CheckoutConfig>({
key: 'checkout-config',
context: { userId: 'u-123' },
fallback: { maxItems: 10, currency: 'USD' },
});

svc.shutdown()

Gracefully tears down the underlying SDK — stops background polling, flushes telemetry, and closes open connections. Call this when your application is shutting down.

process.on('SIGTERM', async () => {
await svc.shutdown();
process.exit(0);
});

Providers

unleashProvider(config)

Adapter for Unleash. Resolves when the SDK has downloaded its initial ruleset. Supports JSON, string, and number variant payloads for getConfig.

import { unleashProvider } from '@open-kerno/commons/feature-management';

const provider = unleashProvider({
url: 'https://unleash.example.com/api',
token: 'my-server-token',
appName: 'my-service',
environment: 'production', // default: 'development'
refreshInterval: 15, // seconds, default: 15
});
OptionTypeRequiredDefaultDescription
urlstringYesUnleash API base URL
tokenstringYesServer-side API token
appNamestringYesApplication name sent in telemetry
environmentstringNo'development'Active environment
refreshIntervalnumberNo15Polling interval in seconds

statsigProvider(config)

Adapter for Statsig. Downloads all gates and dynamic configs during initialization; all subsequent evaluations are local. Maps getConfig to Statsig Dynamic Configs.

import { statsigProvider } from '@open-kerno/commons/feature-management';

const provider = statsigProvider({
serverSecret: process.env.STATSIG_SERVER_SECRET,
environment: 'production', // default: 'development'
});
OptionTypeRequiredDefaultDescription
serverSecretstringYesStatsig server SDK secret key
environmentstringNo'development'Environment tier passed to Statsig

mockProvider(config?)

An in-memory provider for unit tests. Initialization always resolves immediately. Accepts a static map of flags and configs; any key not in the map returns the provided fallback.

import { mockProvider } from '@open-kerno/commons/feature-management';

const provider = mockProvider({
flags: { 'dark-mode': true, 'beta-feature': false },
configs: { 'checkout-config': { maxItems: 5, currency: 'EUR' } },
});
OptionTypeDescription
flagsRecord<string, boolean>Static flag states
configsRecord<string, any>Static config values

Writing a custom provider

Implement the FeatureProvider interface to integrate any other feature flag system:

import {
AreEnabledParams,
FeatureContext,
FeatureProvider,
GetConfigParams,
IsEnabledParams,
} from '@open-kerno/commons/feature-management';

const myProvider: FeatureProvider = {
initialize: async () => {
await mySDK.connect();
},
isEnabled: ({ flag, context }: IsEnabledParams) => mySDK.isEnabled(flag, context?.userId),
areEnabled: ({ flags, context }: AreEnabledParams) => mySDK.areEnabled(flags, context?.userId),
getConfig: <T>({ key, fallback }: GetConfigParams<T>): T => {
return (mySDK.getConfig(key) as T) ?? fallback;
},
shutdown: async () => {
await mySDK.disconnect();
},
};

const svc = await featureManagement(myProvider);

Testing with mockProvider

Inject mockProvider in tests to avoid network calls and control flag states deterministically.

import { featureManagement, mockProvider } from '@open-kerno/commons/feature-management';

describe('checkout', () => {
it('uses new flow when feature flag is on', async () => {
const svc = await featureManagement(
mockProvider({ flags: { 'new-checkout': true } }),
);

const result = await runCheckout(svc);
expect(result.flow).toBe('new');
});
});

Error handling

All evaluations are fail-safe — they never throw. Errors are logged internally via the feature-management logger tag and a safe default is returned instead.

ScenarioisEnabled returnsareEnabled returnsgetConfig returns
Provider init failedfalse[false, ...]fallback
Flag / key not foundfalsefalse per entryfallback
Provider throws during evaluationfalsefalse per entryfallback
Provider not yet readyfalse[false, ...]fallback