Skip to content
View flaglint's full-sized avatar

Block or report flaglint

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
flaglint/README.md

FlagLint

Find every direct LaunchDarkly SDK call. Migrate safely. Enforce the boundary.

CI npm version downloads MIT License


Most teams do not know how many direct LaunchDarkly SDK calls are in their codebase, which ones are safe to migrate, or which ones will silently break if migrated naively. FlagLint answers all three questions before you touch a line of code.

npx flaglint audit ./src
✓ Audit complete: 13 unique flags across 19 call sites — 3 high risk, 10 medium risk

Migration readiness: 53/100  ·  moderate
[█████████████░░░░░░░░░░░░] 53%
10 of 19 call sites safely automatable  ·  9 require manual review

Add --effort-estimate to include a directional planning estimate:

npx flaglint audit ./src --effort-estimate
✓ Audit complete: 13 unique flags across 19 call sites — 3 high risk, 10 medium risk

Migration readiness: 53/100  ·  moderate
[█████████████░░░░░░░░░░░░] 53%
10 of 19 call sites safely automatable  ·  9 require manual review

Estimated migration effort: 20.8h – 40h
Estimates are directional. See the report for assumptions.

For the full report see --format html. Documentation · Quickstart · npm

No API key. No source upload. LaunchDarkly stays your provider — OpenFeature becomes the evaluation API your application calls.

Migrating an existing Node.js service? Read the complete LaunchDarkly-to-OpenFeature migration guide.


The problem FlagLint solves

The OpenFeature getBooleanValue(key, defaultValue, context) API takes arguments in a different order from LaunchDarkly's boolVariation(key, context, defaultValue). A naive find-and-replace silently swaps your fallback and context, producing valid-looking code that evaluates flags incorrectly in production.

FlagLint's static analysis proves — before rewriting anything — that the flag key is static, the fallback value and type are known, and a verified OpenFeature client binding is present. If any condition cannot be proven, the call is reported for manual review and left untouched.


Workflow

Step Command What it does
1 flaglint audit ./src Risk-ranked overview. High / medium / low per flag. No API key needed.
2 flaglint scan ./src Detailed file-level structured inventory for automation or review.
3 flaglint migrate ./src --dry-run Reviewable before/after diffs. Shows exactly what will change.
4 flaglint migrate ./src --apply Rewrites only calls with proven static inputs and a verified OpenFeature binding.
5 flaglint validate ./src --no-direct-launchdarkly CI gate — exits 1 if any direct LD evaluation call remains.

Before and after

--- a/src/routes/checkout.ts
+++ b/src/routes/checkout.ts
-  return ldClient.boolVariation("checkout-v2", ctx, false);
+  return openFeatureClient.getBooleanValue("checkout-v2", false, ctx);

-  return ldClient.stringVariation("payment-provider", ctx, "stripe");
+  return openFeatureClient.getStringValue("payment-provider", "stripe", ctx);

--- a/src/services/pricing.ts
+++ b/src/services/pricing.ts
-  return ldClient.numberVariation("discount-percentage", ctx, 0);
+  return openFeatureClient.getNumberValue("discount-percentage", 0, ctx);

Flag key, fallback value, evaluation context, and await are preserved exactly. The LaunchDarkly packages stay — the OpenFeature provider depends on them at runtime.


What is never auto-rewritten

FlagLint is intentionally conservative. These are always skipped and reported for manual review:

  • Dynamic keysldClient.boolVariation(getFlagKey(user), ctx, false)
  • Detail evaluationsboolVariationDetail, variationDetail
  • Bulk callsallFlags(), allFlagsState()
  • Unknown fallback types
  • Configured wrappers
  • Ambiguous OpenFeature client bindings
  • Browser SDKs, React SDKs, non-Node SDKs

Supported scope

LaunchDarkly Node.js server-side SDK calls from @launchdarkly/node-server-sdk and launchdarkly-node-server-sdk. Both ESM and CommonJS. Node.js 20 or newer.

Full coverage table: Supported Scope


Provider setup (one-time, manual)

Before migrate --apply, complete provider bootstrap once:

import { OpenFeature } from "@openfeature/server-sdk";
import { LaunchDarklyProvider } from "@launchdarkly/openfeature-node-server";

await OpenFeature.setProviderAndWait(
  new LaunchDarklyProvider(process.env.LD_SDK_KEY!)
);
export const openFeatureClient = OpenFeature.getClient();

Evaluation context accepts either targetingKey (OpenFeature-native) or an existing LaunchDarkly key. Do not remove LaunchDarkly packages — the OpenFeature provider depends on them at runtime.

Full instructions: OpenFeature Provider Setup


Local analysis

FlagLint runs entirely on your machine. No source code, flag keys, or file paths leave your environment. No LaunchDarkly API key or credentials are required for audit, scan, migrate, or validate.


Links

Docs · Quickstart · Blog · Security · Contributing · Changelog · License

Pinned Loading

  1. flaglint flaglint Public

    Migrate LaunchDarkly to OpenFeature safely.

    TypeScript 6 4