flaglint 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +6 -5
- package/dist/bin/flaglint.js +40 -14
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## [0.5.2] - 2026-05-27
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Fixed LaunchDarkly client provenance for aliased named `init` imports from both
|
|
15
|
+
supported Node.js server SDK package names. Calls initialized via
|
|
16
|
+
`import { init as ldInit } from "@launchdarkly/node-server-sdk"` or legacy
|
|
17
|
+
`launchdarkly-node-server-sdk` are now consistently detected by `scan`,
|
|
18
|
+
enforced by `validate --no-direct-launchdarkly`, emitted in validation SARIF,
|
|
19
|
+
and handled by existing guarded migration dry-run/apply flows.
|
|
20
|
+
- No migration safety boundary changed: `migrate --apply` still requires a proven
|
|
21
|
+
OpenFeature client binding and continues to skip dynamic keys, detail
|
|
22
|
+
evaluations, bulk calls, unknown fallbacks, and ambiguous cases.
|
|
23
|
+
|
|
24
|
+
## [0.5.1] - 2026-05-27
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Corrected `migrate --dry-run` messaging when all previewed diffs use proven
|
|
29
|
+
OpenFeature client bindings. Dry-run output no longer claims placeholder
|
|
30
|
+
provider/client setup is required when configured imported bindings, aliases,
|
|
31
|
+
or local `OpenFeature.getClient()` bindings are already present.
|
|
32
|
+
- Clarified README and docs scope wording for both supported LaunchDarkly Node.js
|
|
33
|
+
server SDK package names: current `@launchdarkly/node-server-sdk` and legacy
|
|
34
|
+
`launchdarkly-node-server-sdk`.
|
|
35
|
+
- Corrected OpenTelemetry feature-flag semantic-convention guidance to use the
|
|
36
|
+
current `feature_flag.evaluation` event model and current attribute names.
|
|
37
|
+
- Corrected homepage release-state and lower CTA messaging now that `flaglint@0.5.0`
|
|
38
|
+
is published.
|
|
39
|
+
- Narrowed broad flag-debt wording where it could imply comprehensive unused-flag
|
|
40
|
+
lifecycle analysis rather than direct SDK coupling and migration review work.
|
|
41
|
+
|
|
10
42
|
## [0.5.0] - 2026-05-26
|
|
11
43
|
|
|
12
44
|
### Added
|
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ Docs: [Getting Started](https://flaglint.dev/docs/getting-started) · [Commands]
|
|
|
41
41
|
| Step | Command | Purpose |
|
|
42
42
|
|------|---------|---------|
|
|
43
43
|
| 1 | `flaglint scan` | AST inventory of every direct LD Node server SDK call |
|
|
44
|
-
| 2 | `flaglint migrate --dry-run` | Reviewable before/after diffs
|
|
44
|
+
| 2 | `flaglint migrate --dry-run` | Reviewable before/after diffs; provider setup guidance appears when needed |
|
|
45
45
|
| 3 | `flaglint migrate --apply` | Apply only guarded, provably automatable transformations |
|
|
46
46
|
| 4 | `flaglint validate --no-direct-launchdarkly` | CI gate: exit 1 if direct LD calls remain |
|
|
47
47
|
|
|
@@ -141,7 +141,7 @@ flaglint migrate --exclude-tests # skip test and spec files
|
|
|
141
141
|
| Option | Default | Description |
|
|
142
142
|
|--------|---------|-------------|
|
|
143
143
|
| `--output` | `MIGRATION.md` | Write migration plan to file |
|
|
144
|
-
| `--dry-run` | — | Print reviewable diffs to stdout; includes provider setup guidance |
|
|
144
|
+
| `--dry-run` | — | Print reviewable diffs to stdout; includes provider setup guidance when a diff needs it |
|
|
145
145
|
| `--apply` | — | Apply automatable transformations in-place (requires clean git tree) |
|
|
146
146
|
| `--allow-dirty` | — | Override dirty-tree guard for `--apply` |
|
|
147
147
|
| `--config` | auto-detect | Path to a config file |
|
|
@@ -216,7 +216,8 @@ but they are not automatically transformed.
|
|
|
216
216
|
|
|
217
217
|
## Supported API matrix
|
|
218
218
|
|
|
219
|
-
**Scope: LaunchDarkly Node.js server-side SDK
|
|
219
|
+
**Scope: LaunchDarkly Node.js server-side SDK evaluation calls from
|
|
220
|
+
`@launchdarkly/node-server-sdk` and legacy `launchdarkly-node-server-sdk` imports.**
|
|
220
221
|
|
|
221
222
|
| LaunchDarkly call | Automatable | OpenFeature equivalent |
|
|
222
223
|
|---|---|---|
|
|
@@ -439,8 +440,8 @@ reviewers see them in the PR without running anything locally.
|
|
|
439
440
|
|
|
440
441
|
Validated against 120 deterministic benchmark cases within the supported LaunchDarkly Node.js server-side SDK scope. 100% precision and recall are limited to those 120 tested cases and to the Node.js server-side SDK call patterns explicitly listed in the Supported API matrix above.
|
|
441
442
|
|
|
442
|
-
Detection is AST-based, not regex: client binding patterns, import aliases,
|
|
443
|
-
forms are resolved before matching.
|
|
443
|
+
Detection is AST-based, not regex: client binding patterns, named `init` import aliases,
|
|
444
|
+
OpenFeature import aliases, and CJS require forms are resolved before matching.
|
|
444
445
|
|
|
445
446
|
---
|
|
446
447
|
|
package/dist/bin/flaglint.js
CHANGED
|
@@ -178,6 +178,7 @@ function walk(root, visit) {
|
|
|
178
178
|
}
|
|
179
179
|
function collectLDClients(ast) {
|
|
180
180
|
const ldNamespaces = /* @__PURE__ */ new Set();
|
|
181
|
+
const ldInitFunctions = /* @__PURE__ */ new Set();
|
|
181
182
|
for (const stmt of ast.body) {
|
|
182
183
|
if (stmt.type === "ImportDeclaration") {
|
|
183
184
|
const importDecl = stmt;
|
|
@@ -186,6 +187,12 @@ function collectLDClients(ast) {
|
|
|
186
187
|
if (spec.type === "ImportNamespaceSpecifier" || spec.type === "ImportDefaultSpecifier") {
|
|
187
188
|
ldNamespaces.add(spec.local.name);
|
|
188
189
|
}
|
|
190
|
+
if (spec.type === "ImportSpecifier") {
|
|
191
|
+
const importedName = spec.imported.type === "Identifier" ? spec.imported.name : spec.imported.value;
|
|
192
|
+
if (importedName === "init") {
|
|
193
|
+
ldInitFunctions.add(spec.local.name);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
189
196
|
}
|
|
190
197
|
}
|
|
191
198
|
continue;
|
|
@@ -203,7 +210,7 @@ function collectLDClients(ast) {
|
|
|
203
210
|
}
|
|
204
211
|
}
|
|
205
212
|
}
|
|
206
|
-
if (ldNamespaces.size === 0) return /* @__PURE__ */ new Set();
|
|
213
|
+
if (ldNamespaces.size === 0 && ldInitFunctions.size === 0) return /* @__PURE__ */ new Set();
|
|
207
214
|
const ldClients = /* @__PURE__ */ new Set();
|
|
208
215
|
walk(ast, (node) => {
|
|
209
216
|
if (node.type !== "VariableDeclaration") return;
|
|
@@ -211,6 +218,10 @@ function collectLDClients(ast) {
|
|
|
211
218
|
for (const decl of varDecl.declarations) {
|
|
212
219
|
if (decl.id.type !== "Identifier" || !decl.init || decl.init.type !== "CallExpression") continue;
|
|
213
220
|
const initCall = decl.init;
|
|
221
|
+
if (initCall.callee.type === "Identifier" && ldInitFunctions.has(initCall.callee.name)) {
|
|
222
|
+
ldClients.add(decl.id.name);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
214
225
|
if (initCall.callee.type !== "MemberExpression" || initCall.callee.computed) continue;
|
|
215
226
|
const initCallee = initCall.callee;
|
|
216
227
|
if (initCallee.object.type === "Identifier" && initCallee.property.type === "Identifier" && ldNamespaces.has(initCallee.object.name) && initCallee.property.name === "init") {
|
|
@@ -740,7 +751,7 @@ function formatHTML(result, options) {
|
|
|
740
751
|
<div class="card"><div class="card-num orange">${manualCount}</div><div class="card-label">Manual Review (${manualPct}%)</div></div>
|
|
741
752
|
<div class="card"><div class="card-num blue">${detailBulkCount}</div><div class="card-label">Detail/Bulk Calls</div></div>` : "";
|
|
742
753
|
const title = options.title ? esc(options.title) : "FlagLint Scan Report";
|
|
743
|
-
const version = true ? "0.5.
|
|
754
|
+
const version = true ? "0.5.2" : "0.1.0";
|
|
744
755
|
return `<!DOCTYPE html>
|
|
745
756
|
<html lang="en">
|
|
746
757
|
<head>
|
|
@@ -1193,7 +1204,7 @@ function formatMigrationReport(analysis) {
|
|
|
1193
1204
|
unsupportedUnknownCount
|
|
1194
1205
|
} = analysis;
|
|
1195
1206
|
const date = (/* @__PURE__ */ new Date()).toLocaleDateString();
|
|
1196
|
-
const version = true ? "0.5.
|
|
1207
|
+
const version = true ? "0.5.2" : "0.1.0";
|
|
1197
1208
|
const lines = [];
|
|
1198
1209
|
lines.push("# OpenFeature Migration Inventory");
|
|
1199
1210
|
lines.push(`Generated by FlagLint v${version} on ${date}`);
|
|
@@ -1349,7 +1360,7 @@ function itemLabel(item) {
|
|
|
1349
1360
|
}
|
|
1350
1361
|
function formatProviderSetupSection() {
|
|
1351
1362
|
const lines = [];
|
|
1352
|
-
lines.push("## Provider Setup (
|
|
1363
|
+
lines.push("## Provider Setup Guidance (For Diffs Requiring Setup)");
|
|
1353
1364
|
lines.push("");
|
|
1354
1365
|
lines.push("LaunchDarkly remains your feature flag provider.");
|
|
1355
1366
|
lines.push("OpenFeature becomes the evaluation API your application code calls.");
|
|
@@ -1376,7 +1387,7 @@ function formatProviderSetupSection() {
|
|
|
1376
1387
|
lines.push("await OpenFeature.setProviderAndWait(ldProvider);");
|
|
1377
1388
|
lines.push("");
|
|
1378
1389
|
lines.push("// Share this client across your application.");
|
|
1379
|
-
lines.push("// Replace the `openFeatureClient` placeholder in
|
|
1390
|
+
lines.push("// Replace the `openFeatureClient` placeholder in affected diffs.");
|
|
1380
1391
|
lines.push("const openFeatureClient = OpenFeature.getClient();");
|
|
1381
1392
|
lines.push("```");
|
|
1382
1393
|
lines.push("");
|
|
@@ -1417,20 +1428,35 @@ async function formatDryRunDiff(analysis, source, allowedBindings = []) {
|
|
|
1417
1428
|
}
|
|
1418
1429
|
}
|
|
1419
1430
|
const lines = [];
|
|
1431
|
+
const reviewableDiffCount = [...replacementsByFile.values()].reduce((sum, items) => sum + items.length, 0);
|
|
1432
|
+
const setupRequiredCount = [...replacementsByFile.values()].reduce(
|
|
1433
|
+
(sum, items) => sum + items.filter((item) => item.requiresProviderSetup).length,
|
|
1434
|
+
0
|
|
1435
|
+
);
|
|
1420
1436
|
lines.push("# FlagLint migrate --dry-run");
|
|
1421
1437
|
lines.push("");
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1438
|
+
if (reviewableDiffCount > 0 && setupRequiredCount === 0) {
|
|
1439
|
+
lines.push(
|
|
1440
|
+
"The transformations below use proven OpenFeature client bindings already present in the affected files."
|
|
1441
|
+
);
|
|
1442
|
+
} else if (setupRequiredCount > 0 && setupRequiredCount === reviewableDiffCount) {
|
|
1443
|
+
lines.push(
|
|
1444
|
+
"These diffs use the placeholder `openFeatureClient` and require OpenFeature provider/client setup before they can be applied."
|
|
1445
|
+
);
|
|
1446
|
+
} else if (setupRequiredCount > 0) {
|
|
1447
|
+
lines.push(
|
|
1448
|
+
"Some diffs use proven OpenFeature client bindings; diffs using the `openFeatureClient` placeholder require provider/client setup before they can be applied."
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1425
1451
|
lines.push("No files are modified by dry-run output.");
|
|
1426
1452
|
lines.push("");
|
|
1427
|
-
lines.push(`Reviewable diffs: ${
|
|
1428
|
-
lines.push(
|
|
1429
|
-
`Diffs requiring provider setup: ${[...replacementsByFile.values()].reduce((sum, items) => sum + items.filter((item) => item.requiresProviderSetup).length, 0)}`
|
|
1430
|
-
);
|
|
1453
|
+
lines.push(`Reviewable diffs: ${reviewableDiffCount}`);
|
|
1454
|
+
lines.push(`Diffs requiring provider setup: ${setupRequiredCount}`);
|
|
1431
1455
|
lines.push(`Skipped usages: ${skipped.length}`);
|
|
1432
1456
|
lines.push("");
|
|
1433
|
-
|
|
1457
|
+
if (setupRequiredCount > 0) {
|
|
1458
|
+
lines.push(...formatProviderSetupSection());
|
|
1459
|
+
}
|
|
1434
1460
|
if (replacementsByFile.size > 0) {
|
|
1435
1461
|
lines.push("## Diffs");
|
|
1436
1462
|
lines.push("```diff");
|
|
@@ -1942,7 +1968,7 @@ Examples:
|
|
|
1942
1968
|
// src/cli.ts
|
|
1943
1969
|
function createCLI() {
|
|
1944
1970
|
const program2 = new Command();
|
|
1945
|
-
program2.name("flaglint").description("LaunchDarkly Node.js server SDK -> OpenFeature migration.").version("0.5.
|
|
1971
|
+
program2.name("flaglint").description("LaunchDarkly Node.js server SDK -> OpenFeature migration.").version("0.5.2", "-v, --version", "output the current version").addHelpText(
|
|
1946
1972
|
"after",
|
|
1947
1973
|
`
|
|
1948
1974
|
Examples:
|