npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@setu-ts/feature-flags-plugin

v0.11.0

Published

Feature flags with percentage rollout, user targeting, and pluggable providers

Readme

@setu-ts/feature-flags-plugin

Feature flags with pluggable backends. Registers the synchronous IFeatureFlags under CAPABILITIES.FEATURE_FLAGS ('feature-flags').

No eager imports, and the built-in providers (config, memory, database) need no npm package at all. The launchdarkly provider lazy-loads @launchdarkly/node-server-sdk, which npm therefore installs alongside this package; nothing imports it unless you select that provider. See Optional npm drivers for what that means on npm versus Deno.

Providers own a cached snapshot plus an async start()/stop() lifecycle the plugin drives, which is what lets isEnabled stay synchronous at the call site.

Installation

import { createFlagGuard, FeatureFlagsPlugin } from '@setu-ts/feature-flags-plugin';

Usage

import { createFlagGuard, FeatureFlagsPlugin } from '@setu-ts/feature-flags-plugin';
import { CAPABILITIES, type IFeatureFlags } from '@setu-ts/common';

app.register(
  FeatureFlagsPlugin({
    provider: 'config',
    options: {
      flags: {
        'new-checkout': { enabled: false, users: ['user-1', 'user-2'] },
        'dark-mode': { enabled: true, percentage: 25 },
      },
    },
  }),
);

const flags = app.services.get<IFeatureFlags>(CAPABILITIES.FEATURE_FLAGS);
if (flags.isEnabled('new-checkout', { userId: 'user-1' })) {
  /* … */
}

// Route guard — short-circuits without calling next()
app.router.get('/beta', {
  middleware: [createFlagGuard('new-checkout', { fallback: '/' })],
  handler: betaHandler,
});

Options

FeatureFlagsPluginOptions is a union discriminated on provider, so a missing per-arm field is a compile error rather than a startup throw:

| Option | Type | Description | | ---------- | ------------------------------------------------------------------ | --------------------------- | | provider | 'config' \| 'memory' \| 'database' \| 'launchdarkly' \| 'custom' | Selects the arm. | | options | per-arm shape | Configuration for that arm. |

options is required for 'config' ({ flags }), 'database', 'launchdarkly' and 'custom' ({ instance }), and optional for 'memory'. See Providers for behaviour and LaunchDarkly for that arm's fields.

Providers

| provider | Behaviour | | ---------------- | ----------------------------------------------------------------------------- | | 'config' | Immutable inline flags. | | 'memory' | Mutable map — setFlag / removeFlag / replaceFlags. | | 'database' | Polls an injected IFlagStore on one interval; keeps the last good snapshot. | | 'launchdarkly' | LaunchDarkly, via the SDK's one synchronous read — see below. | | 'custom' | Any FlagProvider you supply. |

DatabaseProvider arms a single poll timer in start(). On a poll failure it retains the previous snapshot and reports the degradation rather than flipping every flag off.

Evaluation precedence

Allowlist first, then enabled, then percentage. A users entry overrides enabled: false, so { enabled: false, users: ['user-1'] } turns the flag on for those users only.

The rollout bucket is a deterministic FNV-1a-32 hash of (flag, userId) % 100, so a user's bucket is stable across processes and restarts. A partial rollout with no userId evaluates to false.

Guard behaviour

createFlagGuard(flag, options?) redirects with 302 when fallback is set, otherwise responds with statusCode ?? 404. It never calls next(). An unregistered feature-flags capability is allowed to propagate rather than silently opening the route.

LaunchDarkly

Supported through the 'launchdarkly' arm. The Node server SDK's variation/allFlagsState are async, which no provider can reconcile with the synchronous committed isEnabled directly — the bridge is LDFlagsState.getFlagValue, the SDK's one synchronous read, behind a per-context snapshot cache.

A context whose snapshot has not loaded yet returns fallbackValue and schedules a background refill, coalesced per key so a hot loop over uncached users does not stampede. isEnabledAsync carries no such caveat — it awaits the SDK and is the accurate entry point when a cold read matters.

Set maxCachedContexts alongside provider and options on the 'launchdarkly' plugin arm: FeatureFlagsPlugin({ provider: 'launchdarkly', maxCachedContexts: 1_000, options: { sdkKey } }). The default is 10,000 snapshots. A positive integer is required; invalid limits throw a RangeError naming maxCachedContexts at provider construction. Hits refresh recency; insertion past the limit evicts the least recently used snapshot. An evicted user behaves like a first-time user: fallback while a background refill runs. Direct construction accepts the same limit as the third argument: new LaunchDarklyProvider(config, logger, maxCachedContexts).

| Option | Type | Default | Description | | -------------------- | ------------------------- | ------- | -------------------------------------------------------------------------------------------- | | sdkKey | string | — | Required unless client is injected. | | client | ILaunchDarklyClient | — | Prebuilt client; the SDK module is never loaded. | | fallbackValue | boolean | false | Value for an unloaded context; also the SDK default in isEnabledAsync. | | initTimeoutSeconds | number | 5 | Initial connection wait. A timeout leaves the provider degraded rather than failing startup. | | ldOptions | Record<string, unknown> | — | Forwarded verbatim as the SDK init() second argument. |

Exports

| Export | Kind | | ----------------------------- | --------- | | adaptLaunchDarklyModule | function | | createFlagGuard | function | | createProvider | function | | FeatureFlagsPlugin | function | | loadLaunchDarklyModule | function | | toLaunchDarklyContext | function | | ConfigProvider | class | | DatabaseProvider | class | | FeatureFlagService | class | | LaunchDarklyModuleError | class | | LaunchDarklyProvider | class | | MemoryProvider | class | | ConfigProviderOptions | interface | | CustomProviderOptions | interface | | DatabaseProviderOptions | interface | | FlagContext | interface | | FlagDefinition | interface | | FlagGuardOptions | interface | | FlagProvider | interface | | FlagProviderStatus | interface | | IFeatureFlags | interface | | IFlagStore | interface | | ILaunchDarklyClient | interface | | ILaunchDarklyFlagsState | interface | | ILaunchDarklyModule | interface | | LaunchDarklyContext | interface | | LaunchDarklyProviderConfig | interface | | LaunchDarklyProviderOptions | interface | | MemoryProviderOptions | interface | | FeatureFlagsPluginOptions | type | | FlagProviderType | type |

Generated from the package barrel by pnpm run docs:exports; pnpm run check:docs fails when it drifts.

Full API

Every export and option is documented in PUBLIC_API.md.

Readiness and dependency outages (M113b)

A failed provider keeps feature-flags up with data.healthy: false and data.detail when available. /ready stays 200 while the last good snapshot serves. Alert on data.healthy instead of the overall health status.