flagshark 1.0.0 → 1.1.0

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/dist/cli.js CHANGED
@@ -1,3487 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { execFileSync as execFileSync2 } from "child_process";
5
- import * as fs from "fs";
6
- import * as path from "path";
7
-
8
- // src/detection/interface.ts
9
- var Languages = {
10
- Go: "go",
11
- TypeScript: "typescript",
12
- JavaScript: "javascript",
13
- Python: "python",
14
- Java: "java",
15
- Kotlin: "kotlin",
16
- Swift: "swift",
17
- Ruby: "ruby",
18
- CSharp: "csharp",
19
- PHP: "php",
20
- Rust: "rust",
21
- CPP: "cpp",
22
- ObjectiveC: "objc"
23
- };
24
- function getImportPattern(provider) {
25
- return provider.importPattern || provider.packagePath || "";
26
- }
27
-
28
- // src/detection/helpers.ts
29
- function deduplicateFlags(flags) {
30
- const seen = /* @__PURE__ */ new Set();
31
- const result = [];
32
- for (const flag of flags) {
33
- const key = `${flag.filePath}:${flag.name}:${flag.lineNumber}`;
34
- if (!seen.has(key)) {
35
- seen.add(key);
36
- result.push(flag);
37
- }
38
- }
39
- return result;
40
- }
41
- function extractStringArgument(callText, paramIndex) {
42
- const parenStart = callText.indexOf("(");
43
- if (parenStart === -1) {
44
- return null;
45
- }
46
- const parenEnd = callText.lastIndexOf(")");
47
- if (parenEnd === -1) {
48
- return null;
49
- }
50
- const argsStr = callText.slice(parenStart + 1, parenEnd);
51
- const args = splitArguments(argsStr);
52
- if (paramIndex < 0 || paramIndex >= args.length) {
53
- return null;
54
- }
55
- const arg = args[paramIndex].trim();
56
- const match = arg.match(/^["'`](.*)["'`]$/);
57
- return match ? match[1] : null;
58
- }
59
- function splitArguments(argsStr) {
60
- const args = [];
61
- let depth = 0;
62
- let current = "";
63
- let inString = null;
64
- for (let i = 0; i < argsStr.length; i++) {
65
- const ch = argsStr[i];
66
- const prev = i > 0 ? argsStr[i - 1] : "";
67
- if (inString) {
68
- current += ch;
69
- if (ch === inString && prev !== "\\") {
70
- inString = null;
71
- }
72
- continue;
73
- }
74
- if (ch === '"' || ch === "'" || ch === "`") {
75
- inString = ch;
76
- current += ch;
77
- continue;
78
- }
79
- if (ch === "(" || ch === "[" || ch === "{") {
80
- depth++;
81
- current += ch;
82
- continue;
83
- }
84
- if (ch === ")" || ch === "]" || ch === "}") {
85
- depth--;
86
- current += ch;
87
- continue;
88
- }
89
- if (ch === "," && depth === 0) {
90
- args.push(current);
91
- current = "";
92
- continue;
93
- }
94
- current += ch;
95
- }
96
- if (current.trim().length > 0) {
97
- args.push(current);
98
- }
99
- return args;
100
- }
101
- function escapeRegExp(str) {
102
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
103
- }
104
- function detectFlagsWithRegex(filename, content, language, providers) {
105
- const flags = [];
106
- const lines = content.split("\n");
107
- for (const provider of providers) {
108
- if (!provider.enabled) {
109
- continue;
110
- }
111
- if (provider.methods.length === 0) {
112
- continue;
113
- }
114
- const providerName = provider.name;
115
- const importPat = getImportPattern(provider);
116
- if (importPat) {
117
- const hasImport = content.includes(importPat) || lines.some((line) => line.includes(importPat));
118
- if (!hasImport) {
119
- continue;
120
- }
121
- }
122
- for (const method of provider.methods) {
123
- if (method.flagKeyIndex < 0) {
124
- continue;
125
- }
126
- const pattern = buildSingleMethodPattern(method.name);
127
- for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
128
- const line = lines[lineIdx];
129
- let match;
130
- while ((match = pattern.exec(line)) !== null) {
131
- const callStart = match.index;
132
- const restOfContent = getCallExpression(lines, lineIdx, callStart);
133
- if (!restOfContent) {
134
- continue;
135
- }
136
- const flagKey = extractStringArgument(restOfContent, method.flagKeyIndex);
137
- if (flagKey && isValidFlagKey(flagKey)) {
138
- flags.push({
139
- name: flagKey,
140
- filePath: filename,
141
- lineNumber: lineIdx + 1,
142
- language,
143
- provider: importPat || providerName
144
- });
145
- }
146
- }
147
- }
148
- }
149
- }
150
- return deduplicateFlags(flags);
151
- }
152
- function buildSingleMethodPattern(methodName) {
153
- const escaped = escapeRegExp(methodName);
154
- return new RegExp(`(?:^|[^\\w])(?:\\w+[.:])?${escaped}\\s*\\(`, "g");
155
- }
156
- function getCallExpression(lines, startLine, startCol) {
157
- let result = lines[startLine].slice(startCol);
158
- let depth = 0;
159
- let foundOpen = false;
160
- for (let i = 0; i < result.length; i++) {
161
- if (result[i] === "(") {
162
- depth++;
163
- foundOpen = true;
164
- } else if (result[i] === ")") {
165
- depth--;
166
- if (foundOpen && depth === 0) {
167
- return result.slice(0, i + 1);
168
- }
169
- }
170
- }
171
- const maxLines = Math.min(startLine + 10, lines.length);
172
- for (let lineIdx = startLine + 1; lineIdx < maxLines; lineIdx++) {
173
- const line = lines[lineIdx];
174
- result += "\n" + line;
175
- for (let i = 0; i < line.length; i++) {
176
- if (line[i] === "(") {
177
- depth++;
178
- foundOpen = true;
179
- } else if (line[i] === ")") {
180
- depth--;
181
- if (foundOpen && depth === 0) {
182
- return result;
183
- }
184
- }
185
- }
186
- }
187
- return foundOpen ? result : null;
188
- }
189
- function isValidFlagKey(key) {
190
- if (key.length === 0 || key.length > 256) {
191
- return false;
192
- }
193
- const invalidPrefixes = ["http://", "https://", "file://", "/"];
194
- for (const prefix of invalidPrefixes) {
195
- if (key.startsWith(prefix)) {
196
- return false;
197
- }
198
- }
199
- return true;
200
- }
201
-
202
- // src/detection/detectors/cpp.ts
203
- var CPPDetector = class {
204
- providers;
205
- constructor(providers) {
206
- this.providers = providers ?? defaultCPPProviders();
207
- }
208
- language() {
209
- return Languages.CPP;
210
- }
211
- fileExtensions() {
212
- return [".cpp", ".cc", ".cxx", ".c++", ".hpp", ".hh", ".hxx", ".h++", ".h", ".c"];
213
- }
214
- supportsFile(filename) {
215
- const ext = filename.toLowerCase().split(".").pop();
216
- return ["cpp", "cc", "cxx", "c++", "hpp", "hh", "hxx", "h++", "h", "c"].includes(ext ?? "");
217
- }
218
- detectFlags(filename, content) {
219
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
220
- }
221
- getProviders() {
222
- return this.providers;
223
- }
224
- };
225
- function defaultCPPProviders() {
226
- return [
227
- {
228
- name: "LaunchDarkly C++ SDK",
229
- importPattern: "launchdarkly",
230
- description: "LaunchDarkly C/C++ Server SDK",
231
- enabled: true,
232
- methods: [
233
- {
234
- name: "BoolVariation",
235
- flagKeyIndex: 1,
236
- examples: ['client->BoolVariation(context, "flag-key", false)']
237
- },
238
- {
239
- name: "StringVariation",
240
- flagKeyIndex: 1,
241
- examples: ['client->StringVariation(context, "flag-key", "default")']
242
- },
243
- {
244
- name: "IntVariation",
245
- flagKeyIndex: 1,
246
- examples: ['client->IntVariation(context, "flag-key", 0)']
247
- },
248
- {
249
- name: "DoubleVariation",
250
- flagKeyIndex: 1,
251
- examples: ['client->DoubleVariation(context, "flag-key", 0.0)']
252
- }
253
- ]
254
- },
255
- {
256
- name: "Unleash C++ SDK",
257
- importPattern: "unleash",
258
- description: "Unleash C++ Client SDK",
259
- enabled: true,
260
- methods: [
261
- { name: "isEnabled", flagKeyIndex: 0, examples: ['client->isEnabled("feature-toggle")'] },
262
- { name: "getVariant", flagKeyIndex: 0, examples: ['client->getVariant("feature-toggle")'] }
263
- ]
264
- },
265
- {
266
- name: "ConfigCat C++ SDK",
267
- importPattern: "configcat",
268
- description: "ConfigCat C++ SDK",
269
- enabled: true,
270
- methods: [
271
- { name: "getValue", flagKeyIndex: 0, examples: ['client->getValue("flag-key", false)'] }
272
- ]
273
- },
274
- {
275
- name: "GrowthBook C++ SDK",
276
- importPattern: "growthbook",
277
- description: "GrowthBook C++ SDK",
278
- enabled: true,
279
- methods: [
280
- { name: "isOn", flagKeyIndex: 0, examples: ['gb->isOn("feature-key")'] },
281
- {
282
- name: "getFeatureValue",
283
- flagKeyIndex: 0,
284
- examples: ['gb->getFeatureValue("feature-key", fallbackValue)']
285
- }
286
- ]
287
- },
288
- {
289
- name: "Custom Feature Flags",
290
- description: "Common custom C/C++ feature flag patterns",
291
- enabled: true,
292
- methods: [
293
- {
294
- name: "isFeatureEnabled",
295
- flagKeyIndex: 0,
296
- examples: ['isFeatureEnabled("feature-name")']
297
- },
298
- {
299
- name: "is_feature_enabled",
300
- flagKeyIndex: 0,
301
- examples: ['is_feature_enabled("feature-name")']
302
- },
303
- { name: "checkFeature", flagKeyIndex: 0, examples: ['checkFeature("feature-name")'] },
304
- { name: "hasFeature", flagKeyIndex: 0, examples: ['hasFeature("feature-name")'] }
305
- ]
306
- }
307
- ];
308
- }
309
-
310
- // src/detection/detectors/csharp.ts
311
- var CSharpDetector = class {
312
- providers;
313
- constructor(providers) {
314
- this.providers = providers ?? defaultCSharpProviders();
315
- }
316
- language() {
317
- return Languages.CSharp;
318
- }
319
- fileExtensions() {
320
- return [".cs", ".csx"];
321
- }
322
- supportsFile(filename) {
323
- const lower = filename.toLowerCase();
324
- return lower.endsWith(".cs") || lower.endsWith(".csx");
325
- }
326
- detectFlags(filename, content) {
327
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
328
- }
329
- getProviders() {
330
- return this.providers;
331
- }
332
- };
333
- function defaultCSharpProviders() {
334
- return [
335
- {
336
- name: "LaunchDarkly .NET SDK",
337
- importPattern: "LaunchDarkly.Sdk",
338
- description: "LaunchDarkly .NET SDK",
339
- enabled: true,
340
- methods: [
341
- {
342
- name: "BoolVariation",
343
- flagKeyIndex: 0,
344
- examples: ['client.BoolVariation("flag-key", context, false)']
345
- },
346
- {
347
- name: "StringVariation",
348
- flagKeyIndex: 0,
349
- examples: ['client.StringVariation("flag-key", context, "default")']
350
- },
351
- {
352
- name: "IntVariation",
353
- flagKeyIndex: 0,
354
- examples: ['client.IntVariation("flag-key", context, 0)']
355
- },
356
- {
357
- name: "DoubleVariation",
358
- flagKeyIndex: 0,
359
- examples: ['client.DoubleVariation("flag-key", context, 0.0)']
360
- }
361
- ]
362
- },
363
- {
364
- name: "Unleash .NET SDK",
365
- importPattern: "Unleash",
366
- description: "Unleash .NET SDK",
367
- enabled: true,
368
- methods: [
369
- { name: "IsEnabled", flagKeyIndex: 0, examples: ['unleash.IsEnabled("feature-toggle")'] },
370
- { name: "GetVariant", flagKeyIndex: 0, examples: ['unleash.GetVariant("feature-toggle")'] }
371
- ]
372
- },
373
- {
374
- name: "Split.io .NET SDK",
375
- importPattern: "Splitio.Services.Client",
376
- description: "Split.io .NET SDK",
377
- enabled: true,
378
- methods: [
379
- {
380
- name: "GetTreatment",
381
- flagKeyIndex: 1,
382
- examples: ['client.GetTreatment(key, "split-name")']
383
- },
384
- {
385
- name: "GetTreatments",
386
- flagKeyIndex: 1,
387
- examples: ['client.GetTreatments(key, new List<string>{"split1", "split2"})']
388
- }
389
- ]
390
- },
391
- {
392
- name: "Optimizely .NET SDK",
393
- importPattern: "OptimizelySDK",
394
- description: "Optimizely Feature Experimentation .NET SDK",
395
- enabled: true,
396
- methods: [
397
- { name: "Decide", flagKeyIndex: 0, examples: ['user.Decide("flag-key", options)'] },
398
- {
399
- name: "IsFeatureEnabled",
400
- flagKeyIndex: 0,
401
- examples: ['optimizely.IsFeatureEnabled("feature-key", userId)']
402
- }
403
- ]
404
- },
405
- {
406
- name: "Flagsmith .NET SDK",
407
- importPattern: "Flagsmith",
408
- description: "Flagsmith .NET SDK",
409
- enabled: true,
410
- methods: [
411
- {
412
- name: "HasFeatureFlag",
413
- flagKeyIndex: 0,
414
- examples: ['flags.HasFeatureFlag("feature-name")']
415
- },
416
- {
417
- name: "GetFeatureValue",
418
- flagKeyIndex: 0,
419
- examples: ['flags.GetFeatureValue("feature-name")']
420
- }
421
- ]
422
- },
423
- {
424
- name: "ConfigCat .NET SDK",
425
- importPattern: "ConfigCat.Client",
426
- description: "ConfigCat .NET SDK",
427
- enabled: true,
428
- methods: [
429
- { name: "GetValue", flagKeyIndex: 0, examples: ['client.GetValue("flag-key", false)'] },
430
- {
431
- name: "GetValueAsync",
432
- flagKeyIndex: 0,
433
- examples: ['await client.GetValueAsync("flag-key", false)']
434
- }
435
- ]
436
- },
437
- {
438
- name: "Statsig .NET SDK",
439
- importPattern: "Statsig",
440
- description: "Statsig .NET SDK",
441
- enabled: true,
442
- methods: [
443
- {
444
- name: "CheckGate",
445
- flagKeyIndex: 1,
446
- examples: ['StatsigServer.CheckGate(user, "gate-name")']
447
- },
448
- {
449
- name: "GetExperiment",
450
- flagKeyIndex: 1,
451
- examples: ['StatsigServer.GetExperiment(user, "experiment-name")']
452
- },
453
- {
454
- name: "GetConfig",
455
- flagKeyIndex: 1,
456
- examples: ['StatsigServer.GetConfig(user, "config-name")']
457
- }
458
- ]
459
- },
460
- {
461
- name: "GrowthBook .NET SDK",
462
- importPattern: "GrowthBook",
463
- description: "GrowthBook .NET SDK",
464
- enabled: true,
465
- methods: [
466
- { name: "IsOn", flagKeyIndex: 0, examples: ['gb.IsOn("feature-key")'] },
467
- {
468
- name: "GetFeatureValue",
469
- flagKeyIndex: 0,
470
- examples: ['gb.GetFeatureValue<string>("feature-key", fallbackValue)']
471
- }
472
- ]
473
- },
474
- {
475
- name: "DevCycle .NET SDK",
476
- importPattern: "DevCycle.SDK.Server",
477
- description: "DevCycle .NET SDK",
478
- enabled: true,
479
- methods: [
480
- {
481
- name: "VariableValue",
482
- flagKeyIndex: 1,
483
- examples: ['client.VariableValue(user, "variable-key", defaultValue)']
484
- },
485
- {
486
- name: "Variable",
487
- flagKeyIndex: 1,
488
- examples: ['client.Variable(user, "variable-key", defaultValue)']
489
- }
490
- ]
491
- },
492
- {
493
- name: "Eppo .NET SDK",
494
- importPattern: "Eppo",
495
- description: "Eppo .NET SDK",
496
- enabled: true,
497
- methods: [
498
- {
499
- name: "GetBooleanAssignment",
500
- flagKeyIndex: 0,
501
- examples: ['eppoClient.GetBooleanAssignment("flag-key", subjectKey, defaultValue)']
502
- },
503
- {
504
- name: "GetStringAssignment",
505
- flagKeyIndex: 0,
506
- examples: ['eppoClient.GetStringAssignment("flag-key", subjectKey, defaultValue)']
507
- }
508
- ]
509
- },
510
- {
511
- name: "PostHog .NET SDK",
512
- importPattern: "PostHog",
513
- description: "PostHog .NET SDK",
514
- enabled: true,
515
- methods: [
516
- {
517
- name: "IsFeatureEnabled",
518
- flagKeyIndex: 0,
519
- examples: ['posthog.IsFeatureEnabled("flag-key", distinctId)']
520
- },
521
- {
522
- name: "GetFeatureFlag",
523
- flagKeyIndex: 0,
524
- examples: ['posthog.GetFeatureFlag("flag-key", distinctId)']
525
- }
526
- ]
527
- },
528
- {
529
- name: "Microsoft Feature Management",
530
- importPattern: "Microsoft.FeatureManagement",
531
- description: "Microsoft Feature Management for .NET",
532
- enabled: true,
533
- methods: [
534
- {
535
- name: "IsEnabledAsync",
536
- flagKeyIndex: 0,
537
- examples: ['await featureManager.IsEnabledAsync("feature-name")']
538
- },
539
- {
540
- name: "IsEnabled",
541
- flagKeyIndex: 0,
542
- examples: ['featureManager.IsEnabled("feature-name")']
543
- }
544
- ]
545
- },
546
- {
547
- name: "Custom Feature Flags",
548
- description: "Common custom C# feature flag patterns",
549
- enabled: true,
550
- methods: [
551
- {
552
- name: "IsFeatureEnabled",
553
- flagKeyIndex: 0,
554
- examples: ['IsFeatureEnabled("feature-name")']
555
- },
556
- { name: "CheckFeature", flagKeyIndex: 0, examples: ['CheckFeature("feature-name")'] },
557
- { name: "HasFeature", flagKeyIndex: 0, examples: ['HasFeature("feature-name")'] }
558
- ]
559
- }
560
- ];
561
- }
562
-
563
- // src/detection/detectors/go.ts
564
- var GoDetector = class {
565
- providers;
566
- constructor(providers) {
567
- this.providers = providers ?? defaultGoProviders();
568
- }
569
- language() {
570
- return Languages.Go;
571
- }
572
- fileExtensions() {
573
- return [".go"];
574
- }
575
- supportsFile(filename) {
576
- return filename.toLowerCase().endsWith(".go");
577
- }
578
- detectFlags(filename, content) {
579
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
580
- }
581
- getProviders() {
582
- return this.providers;
583
- }
584
- };
585
- function defaultGoProviders() {
586
- return [
587
- {
588
- name: "LaunchDarkly Go SDK",
589
- packagePath: "github.com/launchdarkly/go-server-sdk/v7",
590
- description: "LaunchDarkly feature flag service",
591
- enabled: true,
592
- methods: [
593
- {
594
- name: "BoolVariation",
595
- flagKeyIndex: 0,
596
- contextIndex: 1,
597
- examples: ['client.BoolVariation("flag-key", context, false)']
598
- },
599
- {
600
- name: "StringVariation",
601
- flagKeyIndex: 0,
602
- contextIndex: 1,
603
- examples: ['client.StringVariation("flag-key", context, "default")']
604
- },
605
- {
606
- name: "IntVariation",
607
- flagKeyIndex: 0,
608
- contextIndex: 1,
609
- examples: ['client.IntVariation("flag-key", context, 0)']
610
- },
611
- {
612
- name: "Float64Variation",
613
- flagKeyIndex: 0,
614
- contextIndex: 1,
615
- examples: ['client.Float64Variation("flag-key", context, 0.0)']
616
- },
617
- {
618
- name: "JSONVariation",
619
- flagKeyIndex: 0,
620
- contextIndex: 1,
621
- examples: ['client.JSONVariation("flag-key", context, ldvalue.Null())']
622
- },
623
- {
624
- name: "BoolVariationDetail",
625
- flagKeyIndex: 0,
626
- contextIndex: 1,
627
- examples: ['client.BoolVariationDetail("flag-key", context, false)']
628
- },
629
- {
630
- name: "StringVariationDetail",
631
- flagKeyIndex: 0,
632
- contextIndex: 1,
633
- examples: ['client.StringVariationDetail("flag-key", context, "default")']
634
- },
635
- {
636
- name: "IntVariationDetail",
637
- flagKeyIndex: 0,
638
- contextIndex: 1,
639
- examples: ['client.IntVariationDetail("flag-key", context, 0)']
640
- },
641
- {
642
- name: "Float64VariationDetail",
643
- flagKeyIndex: 0,
644
- contextIndex: 1,
645
- examples: ['client.Float64VariationDetail("flag-key", context, 0.0)']
646
- },
647
- {
648
- name: "JSONVariationDetail",
649
- flagKeyIndex: 0,
650
- contextIndex: 1,
651
- examples: ['client.JSONVariationDetail("flag-key", context, ldvalue.Null())']
652
- }
653
- ]
654
- },
655
- {
656
- name: "ZeroFlag",
657
- packagePath: "github.com/aviva-zero/zeroflag",
658
- description: "Custom ZeroFlag implementation",
659
- enabled: true,
660
- methods: [
661
- {
662
- name: "Bool",
663
- flagKeyIndex: 1,
664
- contextIndex: 0,
665
- examples: ['zeroflag.Bool(ctx, "feature-flag")']
666
- }
667
- ]
668
- },
669
- {
670
- name: "Unleash",
671
- packagePath: "github.com/Unleash/unleash-client-go/v4",
672
- description: "Unleash feature toggle service",
673
- enabled: true,
674
- methods: [
675
- { name: "IsEnabled", flagKeyIndex: 0, examples: ['unleash.IsEnabled("feature-toggle")'] },
676
- { name: "GetVariant", flagKeyIndex: 0, examples: ['unleash.GetVariant("feature-toggle")'] }
677
- ]
678
- },
679
- {
680
- name: "Flipt",
681
- packagePath: "go.flipt.io/flipt/sdk/go",
682
- description: "Flipt open-source feature flag solution",
683
- enabled: true,
684
- methods: [
685
- {
686
- name: "Boolean",
687
- flagKeyIndex: 1,
688
- contextIndex: 0,
689
- examples: ['flipt.Boolean(ctx, "flag-key", "entity-id", context)']
690
- }
691
- ]
692
- },
693
- {
694
- name: "Split.io",
695
- packagePath: "github.com/splitio/go-client/v6/splitio/client",
696
- description: "Split.io feature flag and experimentation platform",
697
- enabled: true,
698
- methods: [
699
- {
700
- name: "Treatment",
701
- flagKeyIndex: 1,
702
- examples: ['client.Treatment("user-key", "split-name", nil)']
703
- }
704
- ]
705
- },
706
- {
707
- name: "Optimizely Go SDK",
708
- packagePath: "github.com/optimizely/go-sdk/v2",
709
- description: "Optimizely Feature Experimentation Go SDK",
710
- enabled: true,
711
- methods: [
712
- {
713
- name: "Decide",
714
- flagKeyIndex: 0,
715
- contextIndex: 1,
716
- examples: ['user.Decide("flag-key", options)']
717
- }
718
- ]
719
- },
720
- {
721
- name: "Flagsmith Go SDK",
722
- packagePath: "github.com/Flagsmith/flagsmith-go-client/v2",
723
- description: "Flagsmith Go SDK",
724
- enabled: true,
725
- methods: [
726
- {
727
- name: "GetIdentityFlags",
728
- flagKeyIndex: 0,
729
- examples: ['client.GetIdentityFlags(ctx, "identifier", traits)']
730
- },
731
- {
732
- name: "IsFeatureEnabled",
733
- flagKeyIndex: 0,
734
- examples: ['flags.IsFeatureEnabled("feature-name")']
735
- }
736
- ]
737
- },
738
- {
739
- name: "ConfigCat Go SDK",
740
- packagePath: "github.com/configcat/go-sdk/v7",
741
- description: "ConfigCat Go SDK",
742
- enabled: true,
743
- methods: [
744
- {
745
- name: "GetBoolValue",
746
- flagKeyIndex: 0,
747
- examples: ['client.GetBoolValue("flag-key", false, user)']
748
- },
749
- {
750
- name: "GetStringValue",
751
- flagKeyIndex: 0,
752
- examples: ['client.GetStringValue("flag-key", "default", user)']
753
- },
754
- {
755
- name: "GetIntValue",
756
- flagKeyIndex: 0,
757
- examples: ['client.GetIntValue("flag-key", 0, user)']
758
- }
759
- ]
760
- },
761
- {
762
- name: "Statsig Go SDK",
763
- packagePath: "github.com/statsig-io/go-sdk",
764
- description: "Statsig Go SDK",
765
- enabled: true,
766
- methods: [
767
- {
768
- name: "CheckGate",
769
- flagKeyIndex: 1,
770
- contextIndex: 0,
771
- examples: ['statsig.CheckGate(user, "gate-name")']
772
- },
773
- {
774
- name: "GetExperiment",
775
- flagKeyIndex: 1,
776
- contextIndex: 0,
777
- examples: ['statsig.GetExperiment(user, "experiment-name")']
778
- },
779
- {
780
- name: "GetConfig",
781
- flagKeyIndex: 1,
782
- contextIndex: 0,
783
- examples: ['statsig.GetConfig(user, "config-name")']
784
- }
785
- ]
786
- },
787
- {
788
- name: "GrowthBook Go SDK",
789
- packagePath: "github.com/growthbook/growthbook-golang",
790
- description: "GrowthBook Go SDK",
791
- enabled: true,
792
- methods: [
793
- { name: "Feature", flagKeyIndex: 0, examples: ['gb.Feature("feature-key")'] },
794
- { name: "EvalFeature", flagKeyIndex: 0, examples: ['gb.EvalFeature("feature-key")'] }
795
- ]
796
- },
797
- {
798
- name: "DevCycle Go SDK",
799
- packagePath: "github.com/devcyclehq/go-server-sdk",
800
- description: "DevCycle Go SDK",
801
- enabled: true,
802
- methods: [
803
- {
804
- name: "Variable",
805
- flagKeyIndex: 1,
806
- contextIndex: 0,
807
- examples: ['client.Variable(user, "variable-key", defaultValue)']
808
- },
809
- {
810
- name: "VariableValue",
811
- flagKeyIndex: 1,
812
- contextIndex: 0,
813
- examples: ['client.VariableValue(user, "variable-key", defaultValue)']
814
- }
815
- ]
816
- },
817
- {
818
- name: "Eppo Go SDK",
819
- packagePath: "github.com/Eppo-exp/golang-sdk",
820
- description: "Eppo Go SDK",
821
- enabled: true,
822
- methods: [
823
- {
824
- name: "GetBoolAssignment",
825
- flagKeyIndex: 0,
826
- examples: ['eppoClient.GetBoolAssignment("flag-key", subjectKey, defaultValue)']
827
- },
828
- {
829
- name: "GetStringAssignment",
830
- flagKeyIndex: 0,
831
- examples: ['eppoClient.GetStringAssignment("flag-key", subjectKey, defaultValue)']
832
- },
833
- {
834
- name: "GetNumericAssignment",
835
- flagKeyIndex: 0,
836
- examples: ['eppoClient.GetNumericAssignment("flag-key", subjectKey, defaultValue)']
837
- },
838
- {
839
- name: "GetJSONAssignment",
840
- flagKeyIndex: 0,
841
- examples: ['eppoClient.GetJSONAssignment("flag-key", subjectKey, defaultValue)']
842
- }
843
- ]
844
- },
845
- {
846
- name: "PostHog Go SDK",
847
- packagePath: "github.com/posthog/posthog-go",
848
- description: "PostHog Go SDK",
849
- enabled: true,
850
- methods: [
851
- {
852
- name: "IsFeatureEnabled",
853
- flagKeyIndex: 0,
854
- examples: [
855
- 'client.IsFeatureEnabled(posthog.FeatureFlagPayload{Key: "flag-key", DistinctId: distinctId})'
856
- ]
857
- },
858
- {
859
- name: "GetFeatureFlag",
860
- flagKeyIndex: 0,
861
- examples: [
862
- 'client.GetFeatureFlag(posthog.FeatureFlagPayload{Key: "flag-key", DistinctId: distinctId})'
863
- ]
864
- },
865
- {
866
- name: "GetFeatureFlagPayload",
867
- flagKeyIndex: 0,
868
- examples: [
869
- 'client.GetFeatureFlagPayload(posthog.FeatureFlagPayload{Key: "flag-key", DistinctId: distinctId})'
870
- ]
871
- }
872
- ]
873
- }
874
- ];
875
- }
876
-
877
- // src/detection/detectors/java.ts
878
- var JavaDetector = class {
879
- providers;
880
- constructor(providers) {
881
- this.providers = providers ?? defaultJavaProviders();
882
- }
883
- language() {
884
- return Languages.Java;
885
- }
886
- fileExtensions() {
887
- return [".java"];
888
- }
889
- supportsFile(filename) {
890
- return filename.toLowerCase().endsWith(".java");
891
- }
892
- detectFlags(filename, content) {
893
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
894
- }
895
- getProviders() {
896
- return this.providers;
897
- }
898
- };
899
- function defaultJavaProviders() {
900
- return [
901
- {
902
- name: "LaunchDarkly Java SDK",
903
- importPattern: "com.launchdarkly.sdk",
904
- description: "LaunchDarkly Java SDK",
905
- enabled: true,
906
- methods: [
907
- {
908
- name: "boolVariation",
909
- flagKeyIndex: 0,
910
- examples: ['client.boolVariation("flag-key", context, false)']
911
- },
912
- {
913
- name: "stringVariation",
914
- flagKeyIndex: 0,
915
- examples: ['client.stringVariation("flag-key", context, "default")']
916
- },
917
- {
918
- name: "intVariation",
919
- flagKeyIndex: 0,
920
- examples: ['client.intVariation("flag-key", context, 0)']
921
- },
922
- {
923
- name: "doubleVariation",
924
- flagKeyIndex: 0,
925
- examples: ['client.doubleVariation("flag-key", context, 0.0)']
926
- }
927
- ]
928
- },
929
- {
930
- name: "Unleash Java SDK",
931
- importPattern: "io.getunleash",
932
- description: "Unleash Java SDK",
933
- enabled: true,
934
- methods: [
935
- { name: "isEnabled", flagKeyIndex: 0, examples: ['unleash.isEnabled("feature-toggle")'] },
936
- { name: "getVariant", flagKeyIndex: 0, examples: ['unleash.getVariant("feature-toggle")'] }
937
- ]
938
- },
939
- {
940
- name: "Split.io Java SDK",
941
- importPattern: "io.split.client",
942
- description: "Split.io Java SDK",
943
- enabled: true,
944
- methods: [
945
- {
946
- name: "getTreatment",
947
- flagKeyIndex: 1,
948
- examples: ['client.getTreatment(key, "split-name")']
949
- },
950
- {
951
- name: "getTreatments",
952
- flagKeyIndex: 1,
953
- examples: ['client.getTreatments(key, Arrays.asList("split1", "split2"))']
954
- }
955
- ]
956
- },
957
- {
958
- name: "Flipt Java SDK",
959
- importPattern: "io.flipt",
960
- description: "Flipt Java SDK",
961
- enabled: true,
962
- methods: [
963
- {
964
- name: "evaluateBoolean",
965
- flagKeyIndex: 0,
966
- examples: ['flipt.evaluateBoolean("flag-key", entityId, context)']
967
- },
968
- {
969
- name: "evaluateVariant",
970
- flagKeyIndex: 0,
971
- examples: ['flipt.evaluateVariant("flag-key", entityId, context)']
972
- }
973
- ]
974
- },
975
- {
976
- name: "Optimizely Java SDK",
977
- importPattern: "com.optimizely.ab",
978
- description: "Optimizely Feature Experimentation Java SDK",
979
- enabled: true,
980
- methods: [
981
- { name: "decide", flagKeyIndex: 0, examples: ['user.decide("flag-key", options)'] },
982
- {
983
- name: "isFeatureEnabled",
984
- flagKeyIndex: 0,
985
- examples: ['optimizely.isFeatureEnabled("feature-key", userId)']
986
- }
987
- ]
988
- },
989
- {
990
- name: "Flagsmith Java SDK",
991
- importPattern: "com.flagsmith",
992
- description: "Flagsmith Java SDK",
993
- enabled: true,
994
- methods: [
995
- {
996
- name: "isFeatureEnabled",
997
- flagKeyIndex: 0,
998
- examples: ['flags.isFeatureEnabled("feature-name")']
999
- },
1000
- {
1001
- name: "getFeatureValue",
1002
- flagKeyIndex: 0,
1003
- examples: ['flags.getFeatureValue("feature-name")']
1004
- }
1005
- ]
1006
- },
1007
- {
1008
- name: "ConfigCat Java SDK",
1009
- importPattern: "com.configcat",
1010
- description: "ConfigCat Java SDK",
1011
- enabled: true,
1012
- methods: [
1013
- {
1014
- name: "getValue",
1015
- flagKeyIndex: 1,
1016
- examples: ['client.getValue(Boolean.class, "flag-key", false)']
1017
- },
1018
- {
1019
- name: "getValueDetails",
1020
- flagKeyIndex: 1,
1021
- examples: ['client.getValueDetails(Boolean.class, "flag-key", false)']
1022
- }
1023
- ]
1024
- },
1025
- {
1026
- name: "Statsig Java SDK",
1027
- importPattern: "com.statsig",
1028
- description: "Statsig Java SDK",
1029
- enabled: true,
1030
- methods: [
1031
- { name: "checkGate", flagKeyIndex: 1, examples: ['statsig.checkGate(user, "gate-name")'] },
1032
- {
1033
- name: "getExperiment",
1034
- flagKeyIndex: 1,
1035
- examples: ['statsig.getExperiment(user, "experiment-name")']
1036
- },
1037
- {
1038
- name: "getConfig",
1039
- flagKeyIndex: 1,
1040
- examples: ['statsig.getConfig(user, "config-name")']
1041
- }
1042
- ]
1043
- },
1044
- {
1045
- name: "GrowthBook Java SDK",
1046
- importPattern: "growthbook.sdk",
1047
- description: "GrowthBook Java SDK",
1048
- enabled: true,
1049
- methods: [
1050
- { name: "isOn", flagKeyIndex: 0, examples: ['gb.isOn("feature-key")'] },
1051
- {
1052
- name: "getFeatureValue",
1053
- flagKeyIndex: 0,
1054
- examples: ['gb.getFeatureValue("feature-key", defaultValue)']
1055
- },
1056
- { name: "evalFeature", flagKeyIndex: 0, examples: ['gb.evalFeature("feature-key")'] }
1057
- ]
1058
- },
1059
- {
1060
- name: "DevCycle Java SDK",
1061
- importPattern: "com.devcycle",
1062
- description: "DevCycle Java SDK",
1063
- enabled: true,
1064
- methods: [
1065
- {
1066
- name: "variableValue",
1067
- flagKeyIndex: 1,
1068
- examples: ['client.variableValue(user, "variable-key", defaultValue)']
1069
- },
1070
- { name: "variable", flagKeyIndex: 1, examples: ['client.variable(user, "variable-key")'] }
1071
- ]
1072
- },
1073
- {
1074
- name: "Eppo Java SDK",
1075
- importPattern: "com.eppo",
1076
- description: "Eppo Java SDK",
1077
- enabled: true,
1078
- methods: [
1079
- {
1080
- name: "getBooleanAssignment",
1081
- flagKeyIndex: 0,
1082
- examples: ['eppoClient.getBooleanAssignment("flag-key", subjectKey, defaultValue)']
1083
- },
1084
- {
1085
- name: "getStringAssignment",
1086
- flagKeyIndex: 0,
1087
- examples: ['eppoClient.getStringAssignment("flag-key", subjectKey, defaultValue)']
1088
- },
1089
- {
1090
- name: "getJSONAssignment",
1091
- flagKeyIndex: 0,
1092
- examples: ['eppoClient.getJSONAssignment("flag-key", subjectKey, defaultValue)']
1093
- }
1094
- ]
1095
- },
1096
- {
1097
- name: "PostHog Java SDK",
1098
- importPattern: "com.posthog.java",
1099
- description: "PostHog Java SDK",
1100
- enabled: true,
1101
- methods: [
1102
- {
1103
- name: "isFeatureEnabled",
1104
- flagKeyIndex: 0,
1105
- examples: ['posthog.isFeatureEnabled("flag-key", distinctId)']
1106
- },
1107
- {
1108
- name: "getFeatureFlag",
1109
- flagKeyIndex: 0,
1110
- examples: ['posthog.getFeatureFlag("flag-key", distinctId)']
1111
- },
1112
- {
1113
- name: "getFeatureFlagPayload",
1114
- flagKeyIndex: 0,
1115
- examples: ['posthog.getFeatureFlagPayload("flag-key", distinctId)']
1116
- }
1117
- ]
1118
- },
1119
- {
1120
- name: "Custom Feature Flags",
1121
- description: "Common custom Java feature flag patterns",
1122
- enabled: true,
1123
- methods: [
1124
- {
1125
- name: "isFeatureEnabled",
1126
- flagKeyIndex: 0,
1127
- examples: ['isFeatureEnabled("feature-name")']
1128
- },
1129
- { name: "checkFeature", flagKeyIndex: 0, examples: ['checkFeature("feature-name")'] },
1130
- { name: "hasFeature", flagKeyIndex: 0, examples: ['hasFeature("feature-name")'] }
1131
- ]
1132
- }
1133
- ];
1134
- }
1135
-
1136
- // src/detection/detectors/typescript.ts
1137
- var TypeScriptDetector = class {
1138
- providers;
1139
- constructor(providers) {
1140
- this.providers = providers ?? defaultTypeScriptProviders();
1141
- }
1142
- language() {
1143
- return Languages.TypeScript;
1144
- }
1145
- fileExtensions() {
1146
- return [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
1147
- }
1148
- supportsFile(filename) {
1149
- const ext = filename.toLowerCase().split(".").pop();
1150
- return ["ts", "tsx", "js", "jsx", "mjs", "cjs"].includes(ext ?? "");
1151
- }
1152
- detectFlags(filename, content) {
1153
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
1154
- }
1155
- getProviders() {
1156
- return this.providers;
1157
- }
1158
- };
1159
- function defaultTypeScriptProviders() {
1160
- return [
1161
- {
1162
- name: "LaunchDarkly JavaScript SDK",
1163
- importPattern: "@launchdarkly/js-client-sdk",
1164
- description: "LaunchDarkly JavaScript/TypeScript SDK",
1165
- enabled: true,
1166
- methods: [
1167
- {
1168
- name: "variation",
1169
- flagKeyIndex: 0,
1170
- examples: ['client.variation("flag-key", defaultValue)']
1171
- },
1172
- {
1173
- name: "boolVariation",
1174
- flagKeyIndex: 0,
1175
- examples: ['client.boolVariation("flag-key", false)']
1176
- }
1177
- ]
1178
- },
1179
- {
1180
- name: "LaunchDarkly Node Server SDK",
1181
- importPattern: "@launchdarkly/node-server-sdk",
1182
- description: "LaunchDarkly Node.js Server SDK",
1183
- enabled: true,
1184
- methods: [
1185
- {
1186
- name: "variation",
1187
- flagKeyIndex: 0,
1188
- examples: ['client.variation("flag-key", context, defaultValue)']
1189
- },
1190
- {
1191
- name: "boolVariation",
1192
- flagKeyIndex: 0,
1193
- examples: ['client.boolVariation("flag-key", context, false)']
1194
- },
1195
- {
1196
- name: "stringVariation",
1197
- flagKeyIndex: 0,
1198
- examples: ['client.stringVariation("flag-key", context, "default")']
1199
- },
1200
- {
1201
- name: "intVariation",
1202
- flagKeyIndex: 0,
1203
- examples: ['client.intVariation("flag-key", context, 0)']
1204
- },
1205
- {
1206
- name: "doubleVariation",
1207
- flagKeyIndex: 0,
1208
- examples: ['client.doubleVariation("flag-key", context, 0.0)']
1209
- },
1210
- {
1211
- name: "jsonVariation",
1212
- flagKeyIndex: 0,
1213
- examples: ['client.jsonVariation("flag-key", context, {})']
1214
- },
1215
- {
1216
- name: "variationDetail",
1217
- flagKeyIndex: 0,
1218
- examples: ['client.variationDetail("flag-key", context, defaultValue)']
1219
- }
1220
- ]
1221
- },
1222
- {
1223
- name: "LaunchDarkly React SDK",
1224
- importPattern: "@launchdarkly/react-client-sdk",
1225
- description: "LaunchDarkly React SDK",
1226
- enabled: true,
1227
- methods: [
1228
- { name: "useFlags", flagKeyIndex: -1, examples: ["const { flagKey } = useFlags()"] },
1229
- { name: "useLDClient", flagKeyIndex: -1, examples: ["const ldClient = useLDClient()"] }
1230
- ]
1231
- },
1232
- {
1233
- name: "LaunchDarkly Legacy Node SDK",
1234
- importPattern: "launchdarkly-node-server-sdk",
1235
- description: "LaunchDarkly legacy Node.js Server SDK",
1236
- enabled: true,
1237
- methods: [
1238
- {
1239
- name: "variation",
1240
- flagKeyIndex: 0,
1241
- examples: ['client.variation("flag-key", context, defaultValue)']
1242
- },
1243
- {
1244
- name: "boolVariation",
1245
- flagKeyIndex: 0,
1246
- examples: ['client.boolVariation("flag-key", context, false)']
1247
- }
1248
- ]
1249
- },
1250
- {
1251
- name: "Unleash JavaScript SDK",
1252
- importPattern: "unleash-client",
1253
- description: "Unleash JavaScript/TypeScript SDK",
1254
- enabled: true,
1255
- methods: [
1256
- { name: "isEnabled", flagKeyIndex: 0, examples: ['unleash.isEnabled("feature-toggle")'] },
1257
- { name: "getVariant", flagKeyIndex: 0, examples: ['unleash.getVariant("feature-toggle")'] }
1258
- ]
1259
- },
1260
- {
1261
- name: "Split.io JavaScript SDK",
1262
- importPattern: "@splitsoftware/splitio",
1263
- description: "Split.io JavaScript/TypeScript SDK",
1264
- enabled: true,
1265
- methods: [
1266
- {
1267
- name: "getTreatment",
1268
- flagKeyIndex: 1,
1269
- examples: ['client.getTreatment(key, "split-name")']
1270
- }
1271
- ]
1272
- },
1273
- {
1274
- name: "React Feature Flags",
1275
- importPattern: "react-feature-flags",
1276
- description: "React feature flags library",
1277
- enabled: true,
1278
- methods: [{ name: "Flag", flagKeyIndex: 0, examples: ['<Flag name="new-feature">'] }]
1279
- },
1280
- {
1281
- name: "Optimizely JavaScript SDK",
1282
- importPattern: "@optimizely/optimizely-sdk",
1283
- description: "Optimizely Feature Experimentation JavaScript/TypeScript SDK",
1284
- enabled: true,
1285
- methods: [
1286
- { name: "decide", flagKeyIndex: 0, examples: ['user.decide("flag-key", options)'] },
1287
- {
1288
- name: "decideForKeys",
1289
- flagKeyIndex: 0,
1290
- examples: ['user.decideForKeys(["flag-key-1", "flag-key-2"], options)']
1291
- },
1292
- { name: "decideAll", flagKeyIndex: 0, examples: ["user.decideAll(options)"] }
1293
- ]
1294
- },
1295
- {
1296
- name: "Flagsmith JavaScript SDK",
1297
- importPattern: "flagsmith",
1298
- description: "Flagsmith JavaScript/TypeScript SDK",
1299
- enabled: true,
1300
- methods: [
1301
- { name: "hasFeature", flagKeyIndex: 0, examples: ['flagsmith.hasFeature("feature-name")'] },
1302
- { name: "getValue", flagKeyIndex: 0, examples: ['flagsmith.getValue("feature-name")'] }
1303
- ]
1304
- },
1305
- {
1306
- name: "ConfigCat JavaScript SDK",
1307
- importPattern: "configcat-js",
1308
- description: "ConfigCat JavaScript/TypeScript SDK",
1309
- enabled: true,
1310
- methods: [
1311
- {
1312
- name: "getValue",
1313
- flagKeyIndex: 0,
1314
- examples: ['client.getValue("flag-key", defaultValue)']
1315
- },
1316
- {
1317
- name: "getValueAsync",
1318
- flagKeyIndex: 0,
1319
- examples: ['await client.getValueAsync("flag-key", defaultValue)']
1320
- }
1321
- ]
1322
- },
1323
- {
1324
- name: "Flipt JavaScript SDK",
1325
- importPattern: "@flipt-io/flipt-client-js",
1326
- description: "Flipt JavaScript/TypeScript SDK",
1327
- enabled: true,
1328
- methods: [
1329
- {
1330
- name: "evaluateBoolean",
1331
- flagKeyIndex: 0,
1332
- examples: ['client.evaluateBoolean("flag-key", entityId, context)']
1333
- },
1334
- {
1335
- name: "evaluateVariant",
1336
- flagKeyIndex: 0,
1337
- examples: ['client.evaluateVariant("flag-key", entityId, context)']
1338
- }
1339
- ]
1340
- },
1341
- {
1342
- name: "Statsig JavaScript SDK",
1343
- importPattern: "statsig-js",
1344
- description: "Statsig JavaScript/TypeScript SDK",
1345
- enabled: true,
1346
- methods: [
1347
- { name: "checkGate", flagKeyIndex: 0, examples: ['statsig.checkGate("gate-name")'] },
1348
- {
1349
- name: "getExperiment",
1350
- flagKeyIndex: 0,
1351
- examples: ['statsig.getExperiment("experiment-name")']
1352
- },
1353
- { name: "getConfig", flagKeyIndex: 0, examples: ['statsig.getConfig("config-name")'] }
1354
- ]
1355
- },
1356
- {
1357
- name: "GrowthBook JavaScript SDK",
1358
- importPattern: "@growthbook/growthbook",
1359
- description: "GrowthBook JavaScript/TypeScript SDK",
1360
- enabled: true,
1361
- methods: [
1362
- { name: "isOn", flagKeyIndex: 0, examples: ['gb.isOn("feature-key")'] },
1363
- {
1364
- name: "getFeatureValue",
1365
- flagKeyIndex: 0,
1366
- examples: ['gb.getFeatureValue("feature-key", fallbackValue)']
1367
- },
1368
- { name: "evalFeature", flagKeyIndex: 0, examples: ['gb.evalFeature("feature-key")'] }
1369
- ]
1370
- },
1371
- {
1372
- name: "DevCycle JavaScript SDK",
1373
- importPattern: "@devcycle/js-client-sdk",
1374
- description: "DevCycle JavaScript/TypeScript SDK",
1375
- enabled: true,
1376
- methods: [
1377
- {
1378
- name: "variableValue",
1379
- flagKeyIndex: 0,
1380
- examples: ['client.variableValue("variable-key", defaultValue)']
1381
- },
1382
- {
1383
- name: "variable",
1384
- flagKeyIndex: 0,
1385
- examples: ['client.variable("variable-key", defaultValue)']
1386
- }
1387
- ]
1388
- },
1389
- {
1390
- name: "Eppo JavaScript SDK",
1391
- importPattern: "@eppo/js-client-sdk",
1392
- description: "Eppo JavaScript/TypeScript SDK",
1393
- enabled: true,
1394
- methods: [
1395
- {
1396
- name: "getBooleanAssignment",
1397
- flagKeyIndex: 0,
1398
- examples: ['eppoClient.getBooleanAssignment("flag-key", subjectKey, defaultValue)']
1399
- },
1400
- {
1401
- name: "getStringAssignment",
1402
- flagKeyIndex: 0,
1403
- examples: ['eppoClient.getStringAssignment("flag-key", subjectKey, defaultValue)']
1404
- },
1405
- {
1406
- name: "getNumericAssignment",
1407
- flagKeyIndex: 0,
1408
- examples: ['eppoClient.getNumericAssignment("flag-key", subjectKey, defaultValue)']
1409
- },
1410
- {
1411
- name: "getJSONAssignment",
1412
- flagKeyIndex: 0,
1413
- examples: ['eppoClient.getJSONAssignment("flag-key", subjectKey, defaultValue)']
1414
- }
1415
- ]
1416
- },
1417
- {
1418
- name: "PostHog JavaScript SDK",
1419
- importPattern: "posthog-js",
1420
- description: "PostHog JavaScript/TypeScript SDK",
1421
- enabled: true,
1422
- methods: [
1423
- {
1424
- name: "isFeatureEnabled",
1425
- flagKeyIndex: 0,
1426
- examples: ['posthog.isFeatureEnabled("flag-key")']
1427
- },
1428
- {
1429
- name: "getFeatureFlag",
1430
- flagKeyIndex: 0,
1431
- examples: ['posthog.getFeatureFlag("flag-key")']
1432
- },
1433
- {
1434
- name: "getFeatureFlagPayload",
1435
- flagKeyIndex: 0,
1436
- examples: ['posthog.getFeatureFlagPayload("flag-key")']
1437
- }
1438
- ]
1439
- },
1440
- {
1441
- name: "PostHog Node SDK",
1442
- importPattern: "posthog-node",
1443
- description: "PostHog Node.js SDK",
1444
- enabled: true,
1445
- methods: [
1446
- {
1447
- name: "isFeatureEnabled",
1448
- flagKeyIndex: 0,
1449
- examples: ['posthog.isFeatureEnabled("flag-key", distinctId)']
1450
- },
1451
- {
1452
- name: "getFeatureFlag",
1453
- flagKeyIndex: 0,
1454
- examples: ['posthog.getFeatureFlag("flag-key", distinctId)']
1455
- },
1456
- {
1457
- name: "getFeatureFlagPayload",
1458
- flagKeyIndex: 0,
1459
- examples: ['posthog.getFeatureFlagPayload("flag-key", distinctId)']
1460
- },
1461
- { name: "getAllFlags", flagKeyIndex: -1, examples: ["posthog.getAllFlags(distinctId)"] }
1462
- ]
1463
- },
1464
- {
1465
- name: "Custom Feature Flags",
1466
- description: "Common custom feature flag patterns",
1467
- enabled: true,
1468
- methods: [
1469
- {
1470
- name: "isFeatureEnabled",
1471
- flagKeyIndex: 0,
1472
- examples: ['isFeatureEnabled("feature-name")']
1473
- },
1474
- { name: "featureFlag", flagKeyIndex: 0, examples: ['featureFlag("feature-name")'] },
1475
- { name: "hasFeature", flagKeyIndex: 0, examples: ['hasFeature("feature-name")'] }
1476
- ]
1477
- }
1478
- ];
1479
- }
1480
-
1481
- // src/detection/detectors/javascript.ts
1482
- var JavaScriptDetector = class {
1483
- providers;
1484
- constructor(providers) {
1485
- this.providers = providers ?? defaultTypeScriptProviders();
1486
- }
1487
- language() {
1488
- return Languages.JavaScript;
1489
- }
1490
- fileExtensions() {
1491
- return [".js", ".jsx", ".mjs", ".cjs"];
1492
- }
1493
- supportsFile(filename) {
1494
- const ext = filename.toLowerCase().split(".").pop();
1495
- return ["js", "jsx", "mjs", "cjs"].includes(ext ?? "");
1496
- }
1497
- detectFlags(filename, content) {
1498
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
1499
- }
1500
- getProviders() {
1501
- return this.providers;
1502
- }
1503
- };
1504
-
1505
- // src/detection/detectors/kotlin.ts
1506
- var KotlinDetector = class {
1507
- providers;
1508
- constructor(providers) {
1509
- this.providers = providers ?? defaultKotlinProviders();
1510
- }
1511
- language() {
1512
- return Languages.Kotlin;
1513
- }
1514
- fileExtensions() {
1515
- return [".kt", ".kts"];
1516
- }
1517
- supportsFile(filename) {
1518
- const lower = filename.toLowerCase();
1519
- return lower.endsWith(".kt") || lower.endsWith(".kts");
1520
- }
1521
- detectFlags(filename, content) {
1522
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
1523
- }
1524
- getProviders() {
1525
- return this.providers;
1526
- }
1527
- };
1528
- function defaultKotlinProviders() {
1529
- return [
1530
- {
1531
- name: "LaunchDarkly Kotlin SDK",
1532
- importPattern: "com.launchdarkly.sdk",
1533
- description: "LaunchDarkly Kotlin/Android SDK",
1534
- enabled: true,
1535
- methods: [
1536
- {
1537
- name: "boolVariation",
1538
- flagKeyIndex: 0,
1539
- examples: ['client.boolVariation("flag-key", context, false)']
1540
- },
1541
- {
1542
- name: "stringVariation",
1543
- flagKeyIndex: 0,
1544
- examples: ['client.stringVariation("flag-key", context, "default")']
1545
- },
1546
- {
1547
- name: "intVariation",
1548
- flagKeyIndex: 0,
1549
- examples: ['client.intVariation("flag-key", context, 0)']
1550
- }
1551
- ]
1552
- },
1553
- {
1554
- name: "Unleash Kotlin SDK",
1555
- importPattern: "io.getunleash",
1556
- description: "Unleash Kotlin/Android SDK",
1557
- enabled: true,
1558
- methods: [
1559
- { name: "isEnabled", flagKeyIndex: 0, examples: ['unleash.isEnabled("feature-toggle")'] },
1560
- { name: "getVariant", flagKeyIndex: 0, examples: ['unleash.getVariant("feature-toggle")'] }
1561
- ]
1562
- },
1563
- {
1564
- name: "Split.io Kotlin SDK",
1565
- importPattern: "io.split.android",
1566
- description: "Split.io Android/Kotlin SDK",
1567
- enabled: true,
1568
- methods: [
1569
- { name: "getTreatment", flagKeyIndex: 0, examples: ['client.getTreatment("split-name")'] }
1570
- ]
1571
- },
1572
- {
1573
- name: "Flipt Kotlin SDK",
1574
- importPattern: "io.flipt",
1575
- description: "Flipt Kotlin SDK",
1576
- enabled: true,
1577
- methods: [
1578
- {
1579
- name: "evaluateBoolean",
1580
- flagKeyIndex: 0,
1581
- examples: ['flipt.evaluateBoolean("flag-key", entityId, context)']
1582
- },
1583
- {
1584
- name: "evaluateVariant",
1585
- flagKeyIndex: 0,
1586
- examples: ['flipt.evaluateVariant("flag-key", entityId, context)']
1587
- }
1588
- ]
1589
- },
1590
- {
1591
- name: "Optimizely Kotlin SDK",
1592
- importPattern: "com.optimizely.ab",
1593
- description: "Optimizely Feature Experimentation Kotlin SDK",
1594
- enabled: true,
1595
- methods: [
1596
- { name: "decide", flagKeyIndex: 0, examples: ['user.decide("flag-key", options)'] },
1597
- {
1598
- name: "isFeatureEnabled",
1599
- flagKeyIndex: 0,
1600
- examples: ['optimizely.isFeatureEnabled("feature-key", userId)']
1601
- }
1602
- ]
1603
- },
1604
- {
1605
- name: "ConfigCat Kotlin SDK",
1606
- importPattern: "com.configcat",
1607
- description: "ConfigCat Kotlin/Android SDK",
1608
- enabled: true,
1609
- methods: [
1610
- { name: "getValue", flagKeyIndex: 0, examples: ['client.getValue("flag-key", false)'] }
1611
- ]
1612
- },
1613
- {
1614
- name: "Statsig Kotlin SDK",
1615
- importPattern: "com.statsig",
1616
- description: "Statsig Kotlin/Android SDK",
1617
- enabled: true,
1618
- methods: [
1619
- { name: "checkGate", flagKeyIndex: 0, examples: ['statsig.checkGate("gate-name")'] },
1620
- {
1621
- name: "getExperiment",
1622
- flagKeyIndex: 0,
1623
- examples: ['statsig.getExperiment("experiment-name")']
1624
- },
1625
- { name: "getConfig", flagKeyIndex: 0, examples: ['statsig.getConfig("config-name")'] }
1626
- ]
1627
- },
1628
- {
1629
- name: "GrowthBook Kotlin SDK",
1630
- importPattern: "growthbook.sdk",
1631
- description: "GrowthBook Kotlin/Android SDK",
1632
- enabled: true,
1633
- methods: [
1634
- { name: "feature", flagKeyIndex: 0, examples: ['gb.feature("feature-key").on'] },
1635
- { name: "isOn", flagKeyIndex: 0, examples: ['gb.isOn("feature-key")'] }
1636
- ]
1637
- },
1638
- {
1639
- name: "DevCycle Kotlin SDK",
1640
- importPattern: "com.devcycle",
1641
- description: "DevCycle Kotlin/Android SDK",
1642
- enabled: true,
1643
- methods: [
1644
- {
1645
- name: "variableValue",
1646
- flagKeyIndex: 0,
1647
- examples: ['client.variableValue("variable-key", defaultValue)']
1648
- },
1649
- { name: "variable", flagKeyIndex: 0, examples: ['client.variable("variable-key")'] }
1650
- ]
1651
- },
1652
- {
1653
- name: "Eppo Kotlin SDK",
1654
- importPattern: "com.eppo",
1655
- description: "Eppo Kotlin/Android SDK",
1656
- enabled: true,
1657
- methods: [
1658
- {
1659
- name: "getBooleanAssignment",
1660
- flagKeyIndex: 0,
1661
- examples: ['eppoClient.getBooleanAssignment("flag-key", subjectKey, defaultValue)']
1662
- },
1663
- {
1664
- name: "getStringAssignment",
1665
- flagKeyIndex: 0,
1666
- examples: ['eppoClient.getStringAssignment("flag-key", subjectKey, defaultValue)']
1667
- }
1668
- ]
1669
- },
1670
- {
1671
- name: "PostHog Android SDK",
1672
- importPattern: "com.posthog.android",
1673
- description: "PostHog Android/Kotlin SDK",
1674
- enabled: true,
1675
- methods: [
1676
- {
1677
- name: "isFeatureEnabled",
1678
- flagKeyIndex: 0,
1679
- examples: ['PostHog.isFeatureEnabled("flag-key")']
1680
- },
1681
- {
1682
- name: "getFeatureFlag",
1683
- flagKeyIndex: 0,
1684
- examples: ['PostHog.getFeatureFlag("flag-key")']
1685
- },
1686
- {
1687
- name: "getFeatureFlagPayload",
1688
- flagKeyIndex: 0,
1689
- examples: ['PostHog.getFeatureFlagPayload("flag-key")']
1690
- }
1691
- ]
1692
- },
1693
- {
1694
- name: "Custom Feature Flags",
1695
- description: "Common custom Kotlin feature flag patterns",
1696
- enabled: true,
1697
- methods: [
1698
- {
1699
- name: "isFeatureEnabled",
1700
- flagKeyIndex: 0,
1701
- examples: ['isFeatureEnabled("feature-name")']
1702
- },
1703
- { name: "checkFeature", flagKeyIndex: 0, examples: ['checkFeature("feature-name")'] },
1704
- { name: "hasFeature", flagKeyIndex: 0, examples: ['hasFeature("feature-name")'] }
1705
- ]
1706
- }
1707
- ];
1708
- }
1709
-
1710
- // src/detection/detectors/objectivec.ts
1711
- var ObjectiveCDetector = class {
1712
- providers;
1713
- constructor(providers) {
1714
- this.providers = providers ?? defaultObjCProviders();
1715
- }
1716
- language() {
1717
- return Languages.ObjectiveC;
1718
- }
1719
- fileExtensions() {
1720
- return [".m", ".mm", ".h"];
1721
- }
1722
- supportsFile(filename) {
1723
- const lower = filename.toLowerCase();
1724
- return lower.endsWith(".m") || lower.endsWith(".mm") || lower.endsWith(".h");
1725
- }
1726
- detectFlags(filename, content) {
1727
- const flags = detectFlagsWithRegex(filename, content, this.language(), this.providers);
1728
- const validFlags = flags.filter((f) => isValidFlagKey(f.name));
1729
- return deduplicateFlags(validFlags);
1730
- }
1731
- getProviders() {
1732
- return this.providers;
1733
- }
1734
- };
1735
- function defaultObjCProviders() {
1736
- return [
1737
- {
1738
- name: "LaunchDarkly iOS SDK (Objective-C)",
1739
- importPattern: "LaunchDarkly/LDClient.h",
1740
- description: "LaunchDarkly iOS SDK for Objective-C",
1741
- enabled: true,
1742
- methods: [
1743
- {
1744
- name: "boolVariation",
1745
- flagKeyIndex: 0,
1746
- examples: ['[[LDClient get] boolVariation:@"flag-key" defaultValue:NO]']
1747
- },
1748
- {
1749
- name: "boolVariationForKey",
1750
- flagKeyIndex: 0,
1751
- examples: ['[[LDClient get] boolVariationForKey:@"flag-key" defaultValue:NO]']
1752
- },
1753
- {
1754
- name: "stringVariation",
1755
- flagKeyIndex: 0,
1756
- examples: ['[[LDClient get] stringVariation:@"flag-key" defaultValue:@""]']
1757
- },
1758
- {
1759
- name: "stringVariationForKey",
1760
- flagKeyIndex: 0,
1761
- examples: ['[[LDClient get] stringVariationForKey:@"flag-key" defaultValue:@""]']
1762
- },
1763
- {
1764
- name: "numberVariation",
1765
- flagKeyIndex: 0,
1766
- examples: ['[[LDClient get] numberVariation:@"flag-key" defaultValue:@0]']
1767
- },
1768
- {
1769
- name: "numberVariationForKey",
1770
- flagKeyIndex: 0,
1771
- examples: ['[[LDClient get] numberVariationForKey:@"flag-key" defaultValue:@0]']
1772
- },
1773
- {
1774
- name: "jsonVariation",
1775
- flagKeyIndex: 0,
1776
- examples: ['[[LDClient get] jsonVariation:@"flag-key" defaultValue:nil]']
1777
- },
1778
- {
1779
- name: "jsonVariationForKey",
1780
- flagKeyIndex: 0,
1781
- examples: ['[[LDClient get] jsonVariationForKey:@"flag-key" defaultValue:nil]']
1782
- },
1783
- {
1784
- name: "variationDetail",
1785
- flagKeyIndex: 0,
1786
- examples: ['[[LDClient get] variationDetail:@"flag-key" defaultValue:NO]']
1787
- },
1788
- {
1789
- name: "variationDetailForKey",
1790
- flagKeyIndex: 0,
1791
- examples: ['[[LDClient get] variationDetailForKey:@"flag-key" defaultValue:NO]']
1792
- }
1793
- ]
1794
- },
1795
- {
1796
- name: "Unleash iOS SDK (Objective-C)",
1797
- importPattern: "UnleashProxyClientSwift",
1798
- description: "Unleash iOS SDK for Objective-C",
1799
- enabled: true,
1800
- methods: [
1801
- { name: "isEnabled", flagKeyIndex: 0, examples: ['[unleash isEnabled:@"feature-toggle"]'] },
1802
- {
1803
- name: "getVariant",
1804
- flagKeyIndex: 0,
1805
- examples: ['[unleash getVariant:@"feature-toggle"]']
1806
- }
1807
- ]
1808
- },
1809
- {
1810
- name: "Split.io iOS SDK (Objective-C)",
1811
- importPattern: "Split/Split.h",
1812
- description: "Split.io iOS SDK for Objective-C",
1813
- enabled: true,
1814
- methods: [
1815
- {
1816
- name: "getTreatment",
1817
- flagKeyIndex: 0,
1818
- examples: ['[splitClient getTreatment:@"split-name"]']
1819
- },
1820
- {
1821
- name: "getTreatmentWithConfig",
1822
- flagKeyIndex: 0,
1823
- examples: ['[splitClient getTreatmentWithConfig:@"split-name"]']
1824
- },
1825
- {
1826
- name: "getTreatments",
1827
- flagKeyIndex: 0,
1828
- examples: ['[splitClient getTreatments:@[@"split-1", @"split-2"]]']
1829
- }
1830
- ]
1831
- },
1832
- {
1833
- name: "Optimizely iOS SDK (Objective-C)",
1834
- importPattern: "Optimizely/Optimizely.h",
1835
- description: "Optimizely Feature Experimentation iOS SDK for Objective-C",
1836
- enabled: true,
1837
- methods: [
1838
- {
1839
- name: "isFeatureEnabled",
1840
- flagKeyIndex: 0,
1841
- examples: ['[optimizely isFeatureEnabled:@"feature-key" userId:userId attributes:nil]']
1842
- },
1843
- {
1844
- name: "getFeatureVariable",
1845
- flagKeyIndex: 0,
1846
- examples: [
1847
- '[optimizely getFeatureVariable:@"feature-key" variableKey:@"var" userId:userId]'
1848
- ]
1849
- },
1850
- { name: "decide", flagKeyIndex: 0, examples: ['[user decide:@"flag-key" options:nil]'] }
1851
- ]
1852
- },
1853
- {
1854
- name: "Flagsmith iOS SDK (Objective-C)",
1855
- importPattern: "FlagsmithClient",
1856
- description: "Flagsmith iOS SDK for Objective-C",
1857
- enabled: true,
1858
- methods: [
1859
- {
1860
- name: "hasFeatureFlag",
1861
- flagKeyIndex: 0,
1862
- examples: ['[[Flagsmith shared] hasFeatureFlag:@"feature-name"]']
1863
- },
1864
- {
1865
- name: "hasFeatureFlagWithID",
1866
- flagKeyIndex: 0,
1867
- examples: ['[[Flagsmith shared] hasFeatureFlagWithID:@"feature-name"]']
1868
- },
1869
- {
1870
- name: "getValueForFeature",
1871
- flagKeyIndex: 0,
1872
- examples: ['[[Flagsmith shared] getValueForFeature:@"feature-name"]']
1873
- }
1874
- ]
1875
- },
1876
- {
1877
- name: "ConfigCat iOS SDK (Objective-C)",
1878
- importPattern: "ConfigCat/ConfigCat.h",
1879
- description: "ConfigCat iOS SDK for Objective-C",
1880
- enabled: true,
1881
- methods: [
1882
- {
1883
- name: "getValue",
1884
- flagKeyIndex: 0,
1885
- examples: ['[configCatClient getValue:@"flag-key" defaultValue:@NO]']
1886
- },
1887
- {
1888
- name: "getValueForKey",
1889
- flagKeyIndex: 0,
1890
- examples: ['[configCatClient getValueForKey:@"flag-key" defaultValue:@NO]']
1891
- }
1892
- ]
1893
- },
1894
- {
1895
- name: "PostHog iOS SDK (Objective-C)",
1896
- importPattern: "PostHog/PostHog.h",
1897
- description: "PostHog iOS SDK for Objective-C",
1898
- enabled: true,
1899
- methods: [
1900
- {
1901
- name: "isFeatureEnabled",
1902
- flagKeyIndex: 0,
1903
- examples: ['[[PHGPostHog sharedInstance] isFeatureEnabled:@"flag-key"]']
1904
- },
1905
- {
1906
- name: "getFeatureFlag",
1907
- flagKeyIndex: 0,
1908
- examples: ['[[PHGPostHog sharedInstance] getFeatureFlag:@"flag-key"]']
1909
- },
1910
- {
1911
- name: "getFeatureFlagPayload",
1912
- flagKeyIndex: 0,
1913
- examples: ['[[PHGPostHog sharedInstance] getFeatureFlagPayload:@"flag-key"]']
1914
- }
1915
- ]
1916
- },
1917
- {
1918
- name: "Custom Feature Flags (Objective-C)",
1919
- description: "Common custom Objective-C feature flag patterns",
1920
- enabled: true,
1921
- methods: [
1922
- {
1923
- name: "isFeatureEnabled",
1924
- flagKeyIndex: 0,
1925
- examples: ['[featureFlags isFeatureEnabled:@"feature-name"]']
1926
- },
1927
- {
1928
- name: "checkFeature",
1929
- flagKeyIndex: 0,
1930
- examples: ['[featureFlags checkFeature:@"feature-name"]']
1931
- },
1932
- {
1933
- name: "hasFeature",
1934
- flagKeyIndex: 0,
1935
- examples: ['[featureFlags hasFeature:@"feature-name"]']
1936
- },
1937
- {
1938
- name: "featureEnabled",
1939
- flagKeyIndex: 0,
1940
- examples: ['[flags featureEnabled:@"feature-name"]']
1941
- }
1942
- ]
1943
- }
1944
- ];
1945
- }
1946
-
1947
- // src/detection/detectors/php.ts
1948
- var PHPDetector = class {
1949
- providers;
1950
- constructor(providers) {
1951
- this.providers = providers ?? defaultPHPProviders();
1952
- }
1953
- language() {
1954
- return Languages.PHP;
1955
- }
1956
- fileExtensions() {
1957
- return [".php", ".phtml", ".php3", ".php4", ".php5", ".phps"];
1958
- }
1959
- supportsFile(filename) {
1960
- const ext = filename.toLowerCase().split(".").pop();
1961
- return ["php", "phtml", "php3", "php4", "php5", "phps"].includes(ext ?? "");
1962
- }
1963
- detectFlags(filename, content) {
1964
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
1965
- }
1966
- getProviders() {
1967
- return this.providers;
1968
- }
1969
- };
1970
- function defaultPHPProviders() {
1971
- return [
1972
- {
1973
- name: "LaunchDarkly PHP SDK",
1974
- importPattern: "LaunchDarkly\\",
1975
- description: "LaunchDarkly PHP SDK",
1976
- enabled: true,
1977
- methods: [
1978
- {
1979
- name: "variation",
1980
- flagKeyIndex: 0,
1981
- examples: ['$client->variation("flag-key", $context, false)']
1982
- },
1983
- {
1984
- name: "boolVariation",
1985
- flagKeyIndex: 0,
1986
- examples: ['$client->boolVariation("flag-key", $context, false)']
1987
- },
1988
- {
1989
- name: "stringVariation",
1990
- flagKeyIndex: 0,
1991
- examples: ['$client->stringVariation("flag-key", $context, "default")']
1992
- },
1993
- {
1994
- name: "intVariation",
1995
- flagKeyIndex: 0,
1996
- examples: ['$client->intVariation("flag-key", $context, 0)']
1997
- },
1998
- {
1999
- name: "floatVariation",
2000
- flagKeyIndex: 0,
2001
- examples: ['$client->floatVariation("flag-key", $context, 0.0)']
2002
- },
2003
- {
2004
- name: "jsonVariation",
2005
- flagKeyIndex: 0,
2006
- examples: ['$client->jsonVariation("flag-key", $context, [])']
2007
- }
2008
- ]
2009
- },
2010
- {
2011
- name: "Unleash PHP SDK",
2012
- importPattern: "Unleash\\Client",
2013
- description: "Unleash PHP SDK",
2014
- enabled: true,
2015
- methods: [
2016
- { name: "isEnabled", flagKeyIndex: 0, examples: ['$unleash->isEnabled("feature-toggle")'] },
2017
- {
2018
- name: "getVariant",
2019
- flagKeyIndex: 0,
2020
- examples: ['$unleash->getVariant("feature-toggle")']
2021
- }
2022
- ]
2023
- },
2024
- {
2025
- name: "Split.io PHP SDK",
2026
- importPattern: "SplitIO\\",
2027
- description: "Split.io PHP SDK",
2028
- enabled: true,
2029
- methods: [
2030
- {
2031
- name: "getTreatment",
2032
- flagKeyIndex: 1,
2033
- examples: ['$client->getTreatment($key, "split-name")']
2034
- },
2035
- {
2036
- name: "getTreatments",
2037
- flagKeyIndex: 1,
2038
- examples: ['$client->getTreatments($key, ["split1", "split2"])']
2039
- }
2040
- ]
2041
- },
2042
- {
2043
- name: "Flagsmith PHP SDK",
2044
- importPattern: "Flagsmith\\",
2045
- description: "Flagsmith PHP SDK",
2046
- enabled: true,
2047
- methods: [
2048
- {
2049
- name: "isFeatureEnabled",
2050
- flagKeyIndex: 0,
2051
- examples: ['$flags->isFeatureEnabled("feature-name")']
2052
- },
2053
- {
2054
- name: "getFeatureValue",
2055
- flagKeyIndex: 0,
2056
- examples: ['$flags->getFeatureValue("feature-name")']
2057
- }
2058
- ]
2059
- },
2060
- {
2061
- name: "ConfigCat PHP SDK",
2062
- importPattern: "ConfigCat\\",
2063
- description: "ConfigCat PHP SDK",
2064
- enabled: true,
2065
- methods: [
2066
- { name: "getValue", flagKeyIndex: 0, examples: ['$client->getValue("flag-key", false)'] }
2067
- ]
2068
- },
2069
- {
2070
- name: "Statsig PHP SDK",
2071
- importPattern: "Statsig\\",
2072
- description: "Statsig PHP SDK",
2073
- enabled: true,
2074
- methods: [
2075
- {
2076
- name: "checkGate",
2077
- flagKeyIndex: 1,
2078
- examples: ['Statsig::checkGate($user, "gate-name")']
2079
- },
2080
- {
2081
- name: "getExperiment",
2082
- flagKeyIndex: 1,
2083
- examples: ['Statsig::getExperiment($user, "experiment-name")']
2084
- },
2085
- {
2086
- name: "getConfig",
2087
- flagKeyIndex: 1,
2088
- examples: ['Statsig::getConfig($user, "config-name")']
2089
- }
2090
- ]
2091
- },
2092
- {
2093
- name: "GrowthBook PHP SDK",
2094
- importPattern: "Growthbook\\",
2095
- description: "GrowthBook PHP SDK",
2096
- enabled: true,
2097
- methods: [
2098
- { name: "isOn", flagKeyIndex: 0, examples: ['$gb->isOn("feature-key")'] },
2099
- {
2100
- name: "getFeatureValue",
2101
- flagKeyIndex: 0,
2102
- examples: ['$gb->getFeatureValue("feature-key", $fallbackValue)']
2103
- }
2104
- ]
2105
- },
2106
- {
2107
- name: "PostHog PHP SDK",
2108
- importPattern: "PostHog\\",
2109
- description: "PostHog PHP SDK",
2110
- enabled: true,
2111
- methods: [
2112
- {
2113
- name: "isFeatureEnabled",
2114
- flagKeyIndex: 0,
2115
- examples: ['PostHog::isFeatureEnabled("flag-key", $distinctId)']
2116
- },
2117
- {
2118
- name: "getFeatureFlag",
2119
- flagKeyIndex: 0,
2120
- examples: ['PostHog::getFeatureFlag("flag-key", $distinctId)']
2121
- }
2122
- ]
2123
- },
2124
- {
2125
- name: "Laravel Pennant",
2126
- importPattern: "Laravel\\Pennant",
2127
- description: "Laravel Pennant feature flags",
2128
- enabled: true,
2129
- methods: [
2130
- { name: "active", flagKeyIndex: 0, examples: ['Feature::active("feature-name")'] },
2131
- { name: "inactive", flagKeyIndex: 0, examples: ['Feature::inactive("feature-name")'] },
2132
- { name: "value", flagKeyIndex: 0, examples: ['Feature::value("feature-name")'] }
2133
- ]
2134
- },
2135
- {
2136
- name: "Custom Feature Flags",
2137
- description: "Common custom PHP feature flag patterns",
2138
- enabled: true,
2139
- methods: [
2140
- {
2141
- name: "isFeatureEnabled",
2142
- flagKeyIndex: 0,
2143
- examples: ['isFeatureEnabled("feature-name")']
2144
- },
2145
- { name: "feature_enabled", flagKeyIndex: 0, examples: ['feature_enabled("feature-name")'] },
2146
- { name: "hasFeature", flagKeyIndex: 0, examples: ['hasFeature("feature-name")'] }
2147
- ]
2148
- }
2149
- ];
2150
- }
2151
-
2152
- // src/detection/detectors/python.ts
2153
- var PythonDetector = class {
2154
- providers;
2155
- constructor(providers) {
2156
- this.providers = providers ?? defaultPythonProviders();
2157
- }
2158
- language() {
2159
- return Languages.Python;
2160
- }
2161
- fileExtensions() {
2162
- return [".py", ".pyw", ".pyx", ".pyi"];
2163
- }
2164
- supportsFile(filename) {
2165
- const ext = filename.toLowerCase().split(".").pop();
2166
- return ["py", "pyw", "pyx", "pyi"].includes(ext ?? "");
2167
- }
2168
- detectFlags(filename, content) {
2169
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
2170
- }
2171
- getProviders() {
2172
- return this.providers;
2173
- }
2174
- };
2175
- function defaultPythonProviders() {
2176
- return [
2177
- {
2178
- name: "LaunchDarkly Python SDK",
2179
- importPattern: "ldclient",
2180
- description: "LaunchDarkly Python SDK",
2181
- enabled: true,
2182
- methods: [
2183
- {
2184
- name: "variation",
2185
- flagKeyIndex: 0,
2186
- examples: ['client.variation("flag-key", user, default_value)']
2187
- },
2188
- {
2189
- name: "bool_variation",
2190
- flagKeyIndex: 0,
2191
- examples: ['client.bool_variation("flag-key", user, False)']
2192
- }
2193
- ]
2194
- },
2195
- {
2196
- name: "Unleash Python SDK",
2197
- importPattern: "UnleashClient",
2198
- description: "Unleash Python SDK",
2199
- enabled: true,
2200
- methods: [
2201
- { name: "is_enabled", flagKeyIndex: 0, examples: ['unleash.is_enabled("feature-toggle")'] },
2202
- {
2203
- name: "get_variant",
2204
- flagKeyIndex: 0,
2205
- examples: ['unleash.get_variant("feature-toggle")']
2206
- }
2207
- ]
2208
- },
2209
- {
2210
- name: "Split.io Python SDK",
2211
- importPattern: "splitio",
2212
- description: "Split.io Python SDK",
2213
- enabled: true,
2214
- methods: [
2215
- {
2216
- name: "get_treatment",
2217
- flagKeyIndex: 1,
2218
- examples: ['client.get_treatment(key, "split-name")']
2219
- }
2220
- ]
2221
- },
2222
- {
2223
- name: "Flipt Python SDK",
2224
- importPattern: "flipt",
2225
- description: "Flipt Python SDK",
2226
- enabled: true,
2227
- methods: [
2228
- {
2229
- name: "evaluate_boolean",
2230
- flagKeyIndex: 0,
2231
- examples: ['flipt.evaluate_boolean("flag-key", entity_id)']
2232
- },
2233
- {
2234
- name: "evaluate_variant",
2235
- flagKeyIndex: 0,
2236
- examples: ['flipt.evaluate_variant("flag-key", entity_id)']
2237
- }
2238
- ]
2239
- },
2240
- {
2241
- name: "Django Feature Flags",
2242
- importPattern: "django_feature_flags",
2243
- description: "Django feature flags",
2244
- enabled: true,
2245
- methods: [
2246
- { name: "flag_enabled", flagKeyIndex: 0, examples: ['flag_enabled("feature-name")'] },
2247
- { name: "flag_disabled", flagKeyIndex: 0, examples: ['flag_disabled("feature-name")'] }
2248
- ]
2249
- },
2250
- {
2251
- name: "Optimizely Python SDK",
2252
- importPattern: "optimizely",
2253
- description: "Optimizely Feature Experimentation Python SDK",
2254
- enabled: true,
2255
- methods: [
2256
- { name: "decide", flagKeyIndex: 0, examples: ['user.decide("flag-key", options)'] },
2257
- {
2258
- name: "decide_for_keys",
2259
- flagKeyIndex: 0,
2260
- examples: ['user.decide_for_keys(["flag-key-1", "flag-key-2"], options)']
2261
- },
2262
- { name: "decide_all", flagKeyIndex: 0, examples: ["user.decide_all(options)"] }
2263
- ]
2264
- },
2265
- {
2266
- name: "Flagsmith Python SDK",
2267
- importPattern: "flagsmith",
2268
- description: "Flagsmith Python SDK",
2269
- enabled: true,
2270
- methods: [
2271
- {
2272
- name: "is_feature_enabled",
2273
- flagKeyIndex: 0,
2274
- examples: ['identity_flags.is_feature_enabled("feature-name")']
2275
- },
2276
- {
2277
- name: "get_feature_value",
2278
- flagKeyIndex: 0,
2279
- examples: ['identity_flags.get_feature_value("feature-name")']
2280
- }
2281
- ]
2282
- },
2283
- {
2284
- name: "ConfigCat Python SDK",
2285
- importPattern: "configcatclient",
2286
- description: "ConfigCat Python SDK",
2287
- enabled: true,
2288
- methods: [
2289
- {
2290
- name: "get_value",
2291
- flagKeyIndex: 0,
2292
- examples: ['client.get_value("flag-key", default_value)']
2293
- },
2294
- {
2295
- name: "get_value_details",
2296
- flagKeyIndex: 0,
2297
- examples: ['client.get_value_details("flag-key", default_value)']
2298
- }
2299
- ]
2300
- },
2301
- {
2302
- name: "Statsig Python SDK",
2303
- importPattern: "statsig",
2304
- description: "Statsig Python SDK",
2305
- enabled: true,
2306
- methods: [
2307
- {
2308
- name: "check_gate",
2309
- flagKeyIndex: 1,
2310
- examples: ['statsig.check_gate(user, "gate-name")']
2311
- },
2312
- {
2313
- name: "get_experiment",
2314
- flagKeyIndex: 1,
2315
- examples: ['statsig.get_experiment(user, "experiment-name")']
2316
- },
2317
- {
2318
- name: "get_config",
2319
- flagKeyIndex: 1,
2320
- examples: ['statsig.get_config(user, "config-name")']
2321
- }
2322
- ]
2323
- },
2324
- {
2325
- name: "GrowthBook Python SDK",
2326
- importPattern: "growthbook",
2327
- description: "GrowthBook Python SDK",
2328
- enabled: true,
2329
- methods: [
2330
- { name: "is_on", flagKeyIndex: 0, examples: ['gb.is_on("feature-key")'] },
2331
- {
2332
- name: "get_feature_value",
2333
- flagKeyIndex: 0,
2334
- examples: ['gb.get_feature_value("feature-key", fallback_value)']
2335
- },
2336
- { name: "eval_feature", flagKeyIndex: 0, examples: ['gb.eval_feature("feature-key")'] }
2337
- ]
2338
- },
2339
- {
2340
- name: "DevCycle Python SDK",
2341
- importPattern: "devcycle_python_sdk",
2342
- description: "DevCycle Python SDK",
2343
- enabled: true,
2344
- methods: [
2345
- {
2346
- name: "variable_value",
2347
- flagKeyIndex: 1,
2348
- examples: ['client.variable_value(user, "variable-key", default_value)']
2349
- },
2350
- {
2351
- name: "variable",
2352
- flagKeyIndex: 1,
2353
- examples: ['client.variable(user, "variable-key", default_value)']
2354
- }
2355
- ]
2356
- },
2357
- {
2358
- name: "Eppo Python SDK",
2359
- importPattern: "eppo_client",
2360
- description: "Eppo Python SDK",
2361
- enabled: true,
2362
- methods: [
2363
- {
2364
- name: "get_boolean_assignment",
2365
- flagKeyIndex: 0,
2366
- examples: ['eppo_client.get_boolean_assignment("flag-key", subject_key, default_value)']
2367
- },
2368
- {
2369
- name: "get_string_assignment",
2370
- flagKeyIndex: 0,
2371
- examples: ['eppo_client.get_string_assignment("flag-key", subject_key, default_value)']
2372
- },
2373
- {
2374
- name: "get_numeric_assignment",
2375
- flagKeyIndex: 0,
2376
- examples: ['eppo_client.get_numeric_assignment("flag-key", subject_key, default_value)']
2377
- },
2378
- {
2379
- name: "get_json_assignment",
2380
- flagKeyIndex: 0,
2381
- examples: ['eppo_client.get_json_assignment("flag-key", subject_key, default_value)']
2382
- }
2383
- ]
2384
- },
2385
- {
2386
- name: "PostHog Python SDK",
2387
- importPattern: "posthog",
2388
- description: "PostHog Python SDK",
2389
- enabled: true,
2390
- methods: [
2391
- {
2392
- name: "feature_enabled",
2393
- flagKeyIndex: 0,
2394
- examples: ['posthog.feature_enabled("flag-key", distinct_id)']
2395
- },
2396
- {
2397
- name: "get_feature_flag",
2398
- flagKeyIndex: 0,
2399
- examples: ['posthog.get_feature_flag("flag-key", distinct_id)']
2400
- },
2401
- {
2402
- name: "get_feature_flag_payload",
2403
- flagKeyIndex: 0,
2404
- examples: ['posthog.get_feature_flag_payload("flag-key", distinct_id)']
2405
- },
2406
- {
2407
- name: "get_all_flags",
2408
- flagKeyIndex: 0,
2409
- examples: ["posthog.get_all_flags(distinct_id)"]
2410
- }
2411
- ]
2412
- },
2413
- {
2414
- name: "Custom Feature Flags",
2415
- description: "Common custom Python feature flag patterns",
2416
- enabled: true,
2417
- methods: [
2418
- {
2419
- name: "is_feature_enabled",
2420
- flagKeyIndex: 0,
2421
- examples: ['is_feature_enabled("feature-name")']
2422
- },
2423
- { name: "feature_flag", flagKeyIndex: 0, examples: ['feature_flag("feature-name")'] },
2424
- { name: "has_feature", flagKeyIndex: 0, examples: ['has_feature("feature-name")'] },
2425
- { name: "check_feature", flagKeyIndex: 0, examples: ['check_feature("feature-name")'] }
2426
- ]
2427
- }
2428
- ];
2429
- }
2430
-
2431
- // src/detection/detectors/ruby.ts
2432
- var RubyDetector = class {
2433
- providers;
2434
- constructor(providers) {
2435
- this.providers = providers ?? defaultRubyProviders();
2436
- }
2437
- language() {
2438
- return Languages.Ruby;
2439
- }
2440
- fileExtensions() {
2441
- return [".rb", ".rake", ".gemspec"];
2442
- }
2443
- supportsFile(filename) {
2444
- const lower = filename.toLowerCase();
2445
- const ext = lower.split(".").pop();
2446
- if (["rb", "rake", "gemspec"].includes(ext ?? "")) {
2447
- return true;
2448
- }
2449
- const baseName = lower.split("/").pop() ?? "";
2450
- return baseName === "rakefile" || baseName === "gemfile";
2451
- }
2452
- detectFlags(filename, content) {
2453
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
2454
- }
2455
- getProviders() {
2456
- return this.providers;
2457
- }
2458
- };
2459
- function defaultRubyProviders() {
2460
- return [
2461
- {
2462
- name: "LaunchDarkly Ruby SDK",
2463
- importPattern: "launchdarkly-server-sdk",
2464
- description: "LaunchDarkly Ruby SDK",
2465
- enabled: true,
2466
- methods: [
2467
- {
2468
- name: "variation",
2469
- flagKeyIndex: 0,
2470
- examples: ['client.variation("flag-key", context, default_value)']
2471
- },
2472
- {
2473
- name: "variation_detail",
2474
- flagKeyIndex: 0,
2475
- examples: ['client.variation_detail("flag-key", context, default_value)']
2476
- }
2477
- ]
2478
- },
2479
- {
2480
- name: "Unleash Ruby SDK",
2481
- importPattern: "unleash",
2482
- description: "Unleash Ruby SDK",
2483
- enabled: true,
2484
- methods: [
2485
- {
2486
- name: "is_enabled?",
2487
- flagKeyIndex: 0,
2488
- examples: ['UNLEASH.is_enabled?("feature-toggle")']
2489
- },
2490
- {
2491
- name: "enabled?",
2492
- flagKeyIndex: 0,
2493
- examples: ['UNLEASH.enabled?("feature-toggle", context)']
2494
- },
2495
- {
2496
- name: "get_variant",
2497
- flagKeyIndex: 0,
2498
- examples: ['UNLEASH.get_variant("feature-toggle", context)']
2499
- }
2500
- ]
2501
- },
2502
- {
2503
- name: "Split.io Ruby SDK",
2504
- importPattern: "splitclient-rb",
2505
- description: "Split.io Ruby SDK",
2506
- enabled: true,
2507
- methods: [
2508
- {
2509
- name: "get_treatment",
2510
- flagKeyIndex: 1,
2511
- examples: ['client.get_treatment(key, "split-name")']
2512
- },
2513
- {
2514
- name: "get_treatments",
2515
- flagKeyIndex: 1,
2516
- examples: ['client.get_treatments(key, ["split1", "split2"])']
2517
- }
2518
- ]
2519
- },
2520
- {
2521
- name: "Flipper",
2522
- importPattern: "flipper",
2523
- description: "Flipper feature flags for Ruby",
2524
- enabled: true,
2525
- methods: [
2526
- { name: "enabled?", flagKeyIndex: 0, examples: ["Flipper.enabled?(:feature_name)"] },
2527
- { name: "disabled?", flagKeyIndex: 0, examples: ["Flipper.disabled?(:feature_name)"] }
2528
- ]
2529
- },
2530
- {
2531
- name: "Optimizely Ruby SDK",
2532
- importPattern: "optimizely-sdk",
2533
- description: "Optimizely Feature Experimentation Ruby SDK",
2534
- enabled: true,
2535
- methods: [
2536
- { name: "decide", flagKeyIndex: 0, examples: ['user.decide("flag-key")'] },
2537
- {
2538
- name: "is_feature_enabled",
2539
- flagKeyIndex: 0,
2540
- examples: ['optimizely.is_feature_enabled("feature-key", user_id)']
2541
- }
2542
- ]
2543
- },
2544
- {
2545
- name: "Flagsmith Ruby SDK",
2546
- importPattern: "flagsmith",
2547
- description: "Flagsmith Ruby SDK",
2548
- enabled: true,
2549
- methods: [
2550
- {
2551
- name: "is_feature_enabled",
2552
- flagKeyIndex: 0,
2553
- examples: ['flags.is_feature_enabled("feature-name")']
2554
- },
2555
- {
2556
- name: "get_feature_value",
2557
- flagKeyIndex: 0,
2558
- examples: ['flags.get_feature_value("feature-name")']
2559
- }
2560
- ]
2561
- },
2562
- {
2563
- name: "ConfigCat Ruby SDK",
2564
- importPattern: "configcat",
2565
- description: "ConfigCat Ruby SDK",
2566
- enabled: true,
2567
- methods: [
2568
- {
2569
- name: "get_value",
2570
- flagKeyIndex: 0,
2571
- examples: ['client.get_value("flag-key", default_value)']
2572
- }
2573
- ]
2574
- },
2575
- {
2576
- name: "Statsig Ruby SDK",
2577
- importPattern: "statsig",
2578
- description: "Statsig Ruby SDK",
2579
- enabled: true,
2580
- methods: [
2581
- {
2582
- name: "check_gate",
2583
- flagKeyIndex: 1,
2584
- examples: ['Statsig.check_gate(user, "gate-name")']
2585
- },
2586
- {
2587
- name: "get_experiment",
2588
- flagKeyIndex: 1,
2589
- examples: ['Statsig.get_experiment(user, "experiment-name")']
2590
- },
2591
- {
2592
- name: "get_config",
2593
- flagKeyIndex: 1,
2594
- examples: ['Statsig.get_config(user, "config-name")']
2595
- }
2596
- ]
2597
- },
2598
- {
2599
- name: "GrowthBook Ruby SDK",
2600
- importPattern: "growthbook",
2601
- description: "GrowthBook Ruby SDK",
2602
- enabled: true,
2603
- methods: [
2604
- { name: "on?", flagKeyIndex: 0, examples: ["gb.on?(:feature_key)"] },
2605
- {
2606
- name: "feature_value",
2607
- flagKeyIndex: 0,
2608
- examples: ["gb.feature_value(:feature_key, fallback_value)"]
2609
- }
2610
- ]
2611
- },
2612
- {
2613
- name: "DevCycle Ruby SDK",
2614
- importPattern: "devcycle-ruby-server-sdk",
2615
- description: "DevCycle Ruby SDK",
2616
- enabled: true,
2617
- methods: [
2618
- {
2619
- name: "variable_value",
2620
- flagKeyIndex: 1,
2621
- examples: ['client.variable_value(user, "variable-key", default_value)']
2622
- },
2623
- { name: "variable", flagKeyIndex: 1, examples: ['client.variable(user, "variable-key")'] }
2624
- ]
2625
- },
2626
- {
2627
- name: "Eppo Ruby SDK",
2628
- importPattern: "eppo_client",
2629
- description: "Eppo Ruby SDK",
2630
- enabled: true,
2631
- methods: [
2632
- {
2633
- name: "get_boolean_assignment",
2634
- flagKeyIndex: 0,
2635
- examples: ['eppo_client.get_boolean_assignment("flag-key", subject_key, default_value)']
2636
- },
2637
- {
2638
- name: "get_string_assignment",
2639
- flagKeyIndex: 0,
2640
- examples: ['eppo_client.get_string_assignment("flag-key", subject_key, default_value)']
2641
- }
2642
- ]
2643
- },
2644
- {
2645
- name: "PostHog Ruby SDK",
2646
- importPattern: "posthog-ruby",
2647
- description: "PostHog Ruby SDK",
2648
- enabled: true,
2649
- methods: [
2650
- {
2651
- name: "is_feature_enabled",
2652
- flagKeyIndex: 0,
2653
- examples: ['posthog.is_feature_enabled("flag-key", distinct_id)']
2654
- },
2655
- {
2656
- name: "get_feature_flag",
2657
- flagKeyIndex: 0,
2658
- examples: ['posthog.get_feature_flag("flag-key", distinct_id)']
2659
- },
2660
- {
2661
- name: "get_feature_flag_payload",
2662
- flagKeyIndex: 0,
2663
- examples: ['posthog.get_feature_flag_payload("flag-key", distinct_id)']
2664
- }
2665
- ]
2666
- },
2667
- {
2668
- name: "Custom Feature Flags",
2669
- description: "Common custom Ruby feature flag patterns",
2670
- enabled: true,
2671
- methods: [
2672
- {
2673
- name: "feature_enabled?",
2674
- flagKeyIndex: 0,
2675
- examples: ['feature_enabled?("feature-name")']
2676
- },
2677
- { name: "enabled?", flagKeyIndex: 0, examples: ['enabled?("feature-name")'] },
2678
- { name: "has_feature?", flagKeyIndex: 0, examples: ['has_feature?("feature-name")'] }
2679
- ]
2680
- }
2681
- ];
2682
- }
2683
-
2684
- // src/detection/detectors/rust.ts
2685
- var RustDetector = class {
2686
- providers;
2687
- constructor(providers) {
2688
- this.providers = providers ?? defaultRustProviders();
2689
- }
2690
- language() {
2691
- return Languages.Rust;
2692
- }
2693
- fileExtensions() {
2694
- return [".rs"];
2695
- }
2696
- supportsFile(filename) {
2697
- return filename.toLowerCase().endsWith(".rs");
2698
- }
2699
- detectFlags(filename, content) {
2700
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
2701
- }
2702
- getProviders() {
2703
- return this.providers;
2704
- }
2705
- };
2706
- function defaultRustProviders() {
2707
- return [
2708
- {
2709
- name: "LaunchDarkly Rust SDK",
2710
- importPattern: "launchdarkly_server_sdk",
2711
- description: "LaunchDarkly Rust Server SDK",
2712
- enabled: true,
2713
- methods: [
2714
- {
2715
- name: "bool_variation",
2716
- flagKeyIndex: 1,
2717
- examples: ['client.bool_variation(&context, "flag-key", false)']
2718
- },
2719
- {
2720
- name: "string_variation",
2721
- flagKeyIndex: 1,
2722
- examples: ['client.string_variation(&context, "flag-key", "default".to_string())']
2723
- },
2724
- {
2725
- name: "int_variation",
2726
- flagKeyIndex: 1,
2727
- examples: ['client.int_variation(&context, "flag-key", 0)']
2728
- },
2729
- {
2730
- name: "float_variation",
2731
- flagKeyIndex: 1,
2732
- examples: ['client.float_variation(&context, "flag-key", 0.0)']
2733
- }
2734
- ]
2735
- },
2736
- {
2737
- name: "Unleash Rust SDK",
2738
- importPattern: "unleash_api_client",
2739
- description: "Unleash Rust SDK",
2740
- enabled: true,
2741
- methods: [
2742
- {
2743
- name: "is_enabled",
2744
- flagKeyIndex: 0,
2745
- examples: ['client.is_enabled("feature-toggle", None, false)']
2746
- },
2747
- {
2748
- name: "get_variant",
2749
- flagKeyIndex: 0,
2750
- examples: ['client.get_variant("feature-toggle", None)']
2751
- }
2752
- ]
2753
- },
2754
- {
2755
- name: "Flagsmith Rust SDK",
2756
- importPattern: "flagsmith",
2757
- description: "Flagsmith Rust SDK",
2758
- enabled: true,
2759
- methods: [
2760
- {
2761
- name: "is_feature_enabled",
2762
- flagKeyIndex: 0,
2763
- examples: ['flags.is_feature_enabled("feature-name")']
2764
- },
2765
- {
2766
- name: "get_feature_value",
2767
- flagKeyIndex: 0,
2768
- examples: ['flags.get_feature_value("feature-name")']
2769
- }
2770
- ]
2771
- },
2772
- {
2773
- name: "ConfigCat Rust SDK",
2774
- importPattern: "configcat",
2775
- description: "ConfigCat Rust SDK",
2776
- enabled: true,
2777
- methods: [
2778
- {
2779
- name: "get_value",
2780
- flagKeyIndex: 0,
2781
- examples: ['client.get_value("flag-key", false, None).await']
2782
- }
2783
- ]
2784
- },
2785
- {
2786
- name: "GrowthBook Rust SDK",
2787
- importPattern: "growthbook",
2788
- description: "GrowthBook Rust SDK",
2789
- enabled: true,
2790
- methods: [
2791
- { name: "is_on", flagKeyIndex: 0, examples: ['gb.is_on("feature-key")'] },
2792
- {
2793
- name: "get_feature_value",
2794
- flagKeyIndex: 0,
2795
- examples: ['gb.get_feature_value("feature-key", fallback_value)']
2796
- }
2797
- ]
2798
- },
2799
- {
2800
- name: "Eppo Rust SDK",
2801
- importPattern: "eppo",
2802
- description: "Eppo Rust SDK",
2803
- enabled: true,
2804
- methods: [
2805
- {
2806
- name: "get_boolean_assignment",
2807
- flagKeyIndex: 0,
2808
- examples: [
2809
- 'eppo_client.get_boolean_assignment("flag-key", &subject_key, &subject_attributes, default_value)'
2810
- ]
2811
- },
2812
- {
2813
- name: "get_string_assignment",
2814
- flagKeyIndex: 0,
2815
- examples: [
2816
- 'eppo_client.get_string_assignment("flag-key", &subject_key, &subject_attributes, default_value)'
2817
- ]
2818
- }
2819
- ]
2820
- },
2821
- {
2822
- name: "Custom Feature Flags",
2823
- description: "Common custom Rust feature flag patterns",
2824
- enabled: true,
2825
- methods: [
2826
- {
2827
- name: "is_feature_enabled",
2828
- flagKeyIndex: 0,
2829
- examples: ['is_feature_enabled("feature-name")']
2830
- },
2831
- { name: "feature_enabled", flagKeyIndex: 0, examples: ['feature_enabled("feature-name")'] },
2832
- { name: "has_feature", flagKeyIndex: 0, examples: ['has_feature("feature-name")'] }
2833
- ]
2834
- }
2835
- ];
2836
- }
2837
-
2838
- // src/detection/detectors/swift.ts
2839
- var SwiftDetector = class {
2840
- providers;
2841
- constructor(providers) {
2842
- this.providers = providers ?? defaultSwiftProviders();
2843
- }
2844
- language() {
2845
- return Languages.Swift;
2846
- }
2847
- fileExtensions() {
2848
- return [".swift"];
2849
- }
2850
- supportsFile(filename) {
2851
- return filename.toLowerCase().endsWith(".swift");
2852
- }
2853
- detectFlags(filename, content) {
2854
- return detectFlagsWithRegex(filename, content, this.language(), this.providers);
2855
- }
2856
- getProviders() {
2857
- return this.providers;
2858
- }
2859
- };
2860
- function defaultSwiftProviders() {
2861
- return [
2862
- {
2863
- name: "LaunchDarkly iOS SDK",
2864
- importPattern: "LaunchDarkly",
2865
- description: "LaunchDarkly iOS/macOS/tvOS SDK",
2866
- enabled: true,
2867
- methods: [
2868
- {
2869
- name: "variation",
2870
- flagKeyIndex: 0,
2871
- examples: ['LDClient.get()!.variation(forKey: "flag-key", defaultValue: false)']
2872
- },
2873
- {
2874
- name: "boolVariation",
2875
- flagKeyIndex: 0,
2876
- examples: ['client.boolVariation(forKey: "flag-key", defaultValue: false)']
2877
- },
2878
- {
2879
- name: "stringVariation",
2880
- flagKeyIndex: 0,
2881
- examples: ['client.stringVariation(forKey: "flag-key", defaultValue: "")']
2882
- }
2883
- ]
2884
- },
2885
- {
2886
- name: "Unleash iOS SDK",
2887
- importPattern: "UnleashProxyClientSwift",
2888
- description: "Unleash iOS SDK",
2889
- enabled: true,
2890
- methods: [
2891
- {
2892
- name: "isEnabled",
2893
- flagKeyIndex: 0,
2894
- examples: ['unleash.isEnabled(name: "feature-toggle")']
2895
- },
2896
- {
2897
- name: "getVariant",
2898
- flagKeyIndex: 0,
2899
- examples: ['unleash.getVariant(name: "feature-toggle")']
2900
- }
2901
- ]
2902
- },
2903
- {
2904
- name: "Split.io iOS SDK",
2905
- importPattern: "Split",
2906
- description: "Split.io iOS SDK",
2907
- enabled: true,
2908
- methods: [
2909
- { name: "getTreatment", flagKeyIndex: 0, examples: ['client.getTreatment("split-name")'] }
2910
- ]
2911
- },
2912
- {
2913
- name: "Optimizely iOS SDK",
2914
- importPattern: "Optimizely",
2915
- description: "Optimizely Feature Experimentation iOS SDK",
2916
- enabled: true,
2917
- methods: [
2918
- { name: "decide", flagKeyIndex: 0, examples: ['user.decide(key: "flag-key")'] },
2919
- {
2920
- name: "isFeatureEnabled",
2921
- flagKeyIndex: 0,
2922
- examples: ['optimizely.isFeatureEnabled(featureKey: "feature-key", userId: userId)']
2923
- }
2924
- ]
2925
- },
2926
- {
2927
- name: "Flagsmith iOS SDK",
2928
- importPattern: "FlagsmithClient",
2929
- description: "Flagsmith iOS SDK",
2930
- enabled: true,
2931
- methods: [
2932
- {
2933
- name: "hasFeatureFlag",
2934
- flagKeyIndex: 0,
2935
- examples: ['Flagsmith.shared.hasFeatureFlag(withID: "feature-name")']
2936
- },
2937
- {
2938
- name: "getValueForFeature",
2939
- flagKeyIndex: 0,
2940
- examples: ['Flagsmith.shared.getValueForFeature(withID: "feature-name")']
2941
- }
2942
- ]
2943
- },
2944
- {
2945
- name: "ConfigCat iOS SDK",
2946
- importPattern: "ConfigCat",
2947
- description: "ConfigCat iOS SDK",
2948
- enabled: true,
2949
- methods: [
2950
- {
2951
- name: "getValue",
2952
- flagKeyIndex: 0,
2953
- examples: ['client.getValue(for: "flag-key", defaultValue: false)']
2954
- }
2955
- ]
2956
- },
2957
- {
2958
- name: "Statsig iOS SDK",
2959
- importPattern: "StatsigOnDeviceEvaluations",
2960
- description: "Statsig iOS/Swift SDK",
2961
- enabled: true,
2962
- methods: [
2963
- { name: "checkGate", flagKeyIndex: 0, examples: ['statsig.checkGate("gate-name")'] },
2964
- {
2965
- name: "getExperiment",
2966
- flagKeyIndex: 0,
2967
- examples: ['statsig.getExperiment("experiment-name")']
2968
- },
2969
- { name: "getConfig", flagKeyIndex: 0, examples: ['statsig.getConfig("config-name")'] }
2970
- ]
2971
- },
2972
- {
2973
- name: "GrowthBook iOS SDK",
2974
- importPattern: "GrowthBook",
2975
- description: "GrowthBook iOS/Swift SDK",
2976
- enabled: true,
2977
- methods: [
2978
- { name: "isOn", flagKeyIndex: 0, examples: ['gb.isOn(feature: "feature-key")'] },
2979
- {
2980
- name: "getFeatureValue",
2981
- flagKeyIndex: 0,
2982
- examples: ['gb.getFeatureValue(feature: "feature-key", default: JSON("blue"))']
2983
- }
2984
- ]
2985
- },
2986
- {
2987
- name: "DevCycle iOS SDK",
2988
- importPattern: "DevCycle",
2989
- description: "DevCycle iOS/Swift SDK",
2990
- enabled: true,
2991
- methods: [
2992
- {
2993
- name: "variableValue",
2994
- flagKeyIndex: 0,
2995
- examples: ['client.variableValue(key: "variable-key", defaultValue: false)']
2996
- },
2997
- { name: "variable", flagKeyIndex: 0, examples: ['client.variable(key: "variable-key")'] }
2998
- ]
2999
- },
3000
- {
3001
- name: "Eppo iOS SDK",
3002
- importPattern: "EppoClient",
3003
- description: "Eppo iOS/Swift SDK",
3004
- enabled: true,
3005
- methods: [
3006
- {
3007
- name: "getBoolAssignment",
3008
- flagKeyIndex: 0,
3009
- examples: [
3010
- 'eppoClient.getBoolAssignment(flagKey: "flag-key", subjectKey: subjectKey, defaultValue: false)'
3011
- ]
3012
- },
3013
- {
3014
- name: "getStringAssignment",
3015
- flagKeyIndex: 0,
3016
- examples: [
3017
- 'eppoClient.getStringAssignment(flagKey: "flag-key", subjectKey: subjectKey, defaultValue: "")'
3018
- ]
3019
- }
3020
- ]
3021
- },
3022
- {
3023
- name: "PostHog iOS SDK",
3024
- importPattern: "PostHog",
3025
- description: "PostHog iOS/Swift SDK",
3026
- enabled: true,
3027
- methods: [
3028
- {
3029
- name: "isFeatureEnabled",
3030
- flagKeyIndex: 0,
3031
- examples: ['PostHogSDK.shared.isFeatureEnabled("flag-key")']
3032
- },
3033
- {
3034
- name: "getFeatureFlag",
3035
- flagKeyIndex: 0,
3036
- examples: ['PostHogSDK.shared.getFeatureFlag("flag-key")']
3037
- },
3038
- {
3039
- name: "getFeatureFlagPayload",
3040
- flagKeyIndex: 0,
3041
- examples: ['PostHogSDK.shared.getFeatureFlagPayload("flag-key")']
3042
- }
3043
- ]
3044
- },
3045
- {
3046
- name: "Custom Feature Flags",
3047
- description: "Common custom Swift feature flag patterns",
3048
- enabled: true,
3049
- methods: [
3050
- {
3051
- name: "isFeatureEnabled",
3052
- flagKeyIndex: 0,
3053
- examples: ['isFeatureEnabled("feature-name")']
3054
- },
3055
- { name: "checkFeature", flagKeyIndex: 0, examples: ['checkFeature("feature-name")'] },
3056
- { name: "hasFeature", flagKeyIndex: 0, examples: ['hasFeature("feature-name")'] }
3057
- ]
3058
- }
3059
- ];
3060
- }
3061
-
3062
- // src/detection/registry.ts
3063
- var LanguageRegistry = class {
3064
- detectors = /* @__PURE__ */ new Map();
3065
- /** Registers a language detector. Throws if a detector for the language is already registered. */
3066
- register(detector) {
3067
- const lang = detector.language();
3068
- if (this.detectors.has(lang)) {
3069
- throw new Error(`detector for language ${lang} already registered`);
3070
- }
3071
- this.detectors.set(lang, detector);
3072
- }
3073
- /** Returns the detector for a specific language, or undefined if not registered. */
3074
- getDetector(lang) {
3075
- return this.detectors.get(lang);
3076
- }
3077
- /** Returns the appropriate detector for a file based on its extension. */
3078
- getDetectorForFile(filename) {
3079
- for (const detector of this.detectors.values()) {
3080
- if (detector.supportsFile(filename)) {
3081
- return detector;
3082
- }
3083
- }
3084
- return void 0;
3085
- }
3086
- /** Detects feature flags in a file using the appropriate language detector. */
3087
- detectInFile(filename, content) {
3088
- const detector = this.getDetectorForFile(filename);
3089
- if (!detector) {
3090
- return null;
3091
- }
3092
- return detector.detectFlags(filename, content);
3093
- }
3094
- /** Returns all registered languages. */
3095
- getSupportedLanguages() {
3096
- return Array.from(this.detectors.keys());
3097
- }
3098
- /** Returns all supported file extensions (deduplicated). */
3099
- getSupportedExtensions() {
3100
- const extensionSet = /* @__PURE__ */ new Set();
3101
- for (const detector of this.detectors.values()) {
3102
- for (const ext of detector.fileExtensions()) {
3103
- extensionSet.add(ext);
3104
- }
3105
- }
3106
- return Array.from(extensionSet);
3107
- }
3108
- };
3109
-
3110
- // node_modules/yocto-queue/index.js
3111
- var Node = class {
3112
- value;
3113
- next;
3114
- constructor(value) {
3115
- this.value = value;
3116
- }
3117
- };
3118
- var Queue = class {
3119
- #head;
3120
- #tail;
3121
- #size;
3122
- constructor() {
3123
- this.clear();
3124
- }
3125
- enqueue(value) {
3126
- const node = new Node(value);
3127
- if (this.#head) {
3128
- this.#tail.next = node;
3129
- this.#tail = node;
3130
- } else {
3131
- this.#head = node;
3132
- this.#tail = node;
3133
- }
3134
- this.#size++;
3135
- }
3136
- dequeue() {
3137
- const current = this.#head;
3138
- if (!current) {
3139
- return;
3140
- }
3141
- this.#head = this.#head.next;
3142
- this.#size--;
3143
- if (!this.#head) {
3144
- this.#tail = void 0;
3145
- }
3146
- return current.value;
3147
- }
3148
- peek() {
3149
- if (!this.#head) {
3150
- return;
3151
- }
3152
- return this.#head.value;
3153
- }
3154
- clear() {
3155
- this.#head = void 0;
3156
- this.#tail = void 0;
3157
- this.#size = 0;
3158
- }
3159
- get size() {
3160
- return this.#size;
3161
- }
3162
- *[Symbol.iterator]() {
3163
- let current = this.#head;
3164
- while (current) {
3165
- yield current.value;
3166
- current = current.next;
3167
- }
3168
- }
3169
- *drain() {
3170
- while (this.#head) {
3171
- yield this.dequeue();
3172
- }
3173
- }
3174
- };
3175
-
3176
- // node_modules/p-limit/index.js
3177
- function pLimit(concurrency) {
3178
- validateConcurrency(concurrency);
3179
- const queue = new Queue();
3180
- let activeCount = 0;
3181
- const resumeNext = () => {
3182
- if (activeCount < concurrency && queue.size > 0) {
3183
- queue.dequeue()();
3184
- activeCount++;
3185
- }
3186
- };
3187
- const next = () => {
3188
- activeCount--;
3189
- resumeNext();
3190
- };
3191
- const run = async (function_, resolve2, arguments_) => {
3192
- const result = (async () => function_(...arguments_))();
3193
- resolve2(result);
3194
- try {
3195
- await result;
3196
- } catch {
3197
- }
3198
- next();
3199
- };
3200
- const enqueue = (function_, resolve2, arguments_) => {
3201
- new Promise((internalResolve) => {
3202
- queue.enqueue(internalResolve);
3203
- }).then(
3204
- run.bind(void 0, function_, resolve2, arguments_)
3205
- );
3206
- (async () => {
3207
- await Promise.resolve();
3208
- if (activeCount < concurrency) {
3209
- resumeNext();
3210
- }
3211
- })();
3212
- };
3213
- const generator = (function_, ...arguments_) => new Promise((resolve2) => {
3214
- enqueue(function_, resolve2, arguments_);
3215
- });
3216
- Object.defineProperties(generator, {
3217
- activeCount: {
3218
- get: () => activeCount
3219
- },
3220
- pendingCount: {
3221
- get: () => queue.size
3222
- },
3223
- clearQueue: {
3224
- value() {
3225
- queue.clear();
3226
- }
3227
- },
3228
- concurrency: {
3229
- get: () => concurrency,
3230
- set(newConcurrency) {
3231
- validateConcurrency(newConcurrency);
3232
- concurrency = newConcurrency;
3233
- queueMicrotask(() => {
3234
- while (activeCount < concurrency && queue.size > 0) {
3235
- resumeNext();
3236
- }
3237
- });
3238
- }
3239
- }
3240
- });
3241
- return generator;
3242
- }
3243
- function validateConcurrency(concurrency) {
3244
- if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
3245
- throw new TypeError("Expected `concurrency` to be a number from 1 and up");
3246
- }
3247
- }
3248
-
3249
- // src/detection/polyglot-analyzer.ts
3250
- var DEFAULT_WORKER_POOL_SIZE = 10;
3251
- var MAX_FILE_SIZE = 5 * 1024 * 1024;
3252
- var PolyglotAnalyzer = class {
3253
- registry;
3254
- logger;
3255
- constructor(registry, logger) {
3256
- this.registry = registry;
3257
- this.logger = logger;
3258
- }
3259
- /** Analyzes multiple files using appropriate language detectors. */
3260
- async analyzeFiles(files, signal) {
3261
- return this.analyzeFilesWithProgress(files, void 0, signal);
3262
- }
3263
- /** Analyzes multiple files with progress reporting. */
3264
- async analyzeFilesWithProgress(files, progressCallback, signal) {
3265
- const result = {
3266
- files: /* @__PURE__ */ new Map(),
3267
- totalFlags: /* @__PURE__ */ new Map(),
3268
- languages: /* @__PURE__ */ new Map(),
3269
- skippedFiles: [],
3270
- partialFiles: []
3271
- };
3272
- const workerPoolSize = Number(process.env.ANALYZER_WORKER_POOL_SIZE) || DEFAULT_WORKER_POOL_SIZE;
3273
- const limit = pLimit(workerPoolSize);
3274
- this.logger.debug("Using analyzer worker pool size", {
3275
- workerPoolSize,
3276
- filesToAnalyze: files.size
3277
- });
3278
- let filesAnalyzed = 0;
3279
- let filesWithFlags = 0;
3280
- let totalFlagsFound = 0;
3281
- let errorCount = 0;
3282
- let lastEmittedPercent = 0;
3283
- const totalFiles = files.size;
3284
- const tasks = [];
3285
- for (const [filePath, content] of files) {
3286
- if (signal?.aborted) {
3287
- break;
3288
- }
3289
- tasks.push(
3290
- limit(async () => {
3291
- if (signal?.aborted) {
3292
- return;
3293
- }
3294
- const fileResult = await this.analyzeFile(filePath, content);
3295
- filesAnalyzed++;
3296
- result.files.set(filePath, fileResult);
3297
- let flagsInFile = 0;
3298
- for (const flag of fileResult.flags) {
3299
- flag.language = fileResult.language;
3300
- const existing = result.totalFlags.get(flag.name) ?? [];
3301
- existing.push(flag);
3302
- result.totalFlags.set(flag.name, existing);
3303
- flagsInFile++;
3304
- totalFlagsFound++;
3305
- }
3306
- if (flagsInFile > 0) {
3307
- filesWithFlags++;
3308
- }
3309
- if (fileResult.parseErrors.length > 0) {
3310
- errorCount++;
3311
- }
3312
- if (fileResult.status === "skipped") {
3313
- result.skippedFiles.push(filePath);
3314
- } else if (fileResult.status === "partial") {
3315
- result.partialFiles.push(filePath);
3316
- }
3317
- if (fileResult.language) {
3318
- const count = result.languages.get(fileResult.language) ?? 0;
3319
- result.languages.set(fileResult.language, count + 1);
3320
- }
3321
- if (progressCallback && totalFiles > 0) {
3322
- const currentPercent = Math.floor(filesAnalyzed * 100 / totalFiles);
3323
- const roundedPercent = Math.floor(currentPercent / 5) * 5;
3324
- if (roundedPercent > lastEmittedPercent || filesAnalyzed === totalFiles) {
3325
- lastEmittedPercent = roundedPercent;
3326
- progressCallback(filesAnalyzed, totalFiles);
3327
- }
3328
- }
3329
- })
3330
- );
3331
- }
3332
- await Promise.allSettled(tasks);
3333
- if (totalFlagsFound > 0 || errorCount > 0) {
3334
- this.logger.info("Polyglot analysis completed", {
3335
- totalFiles: filesAnalyzed,
3336
- filesWithFlags,
3337
- totalFlagsFound,
3338
- uniqueFlags: result.totalFlags.size,
3339
- filesWithErrors: errorCount,
3340
- languages: Object.fromEntries(result.languages)
3341
- });
3342
- }
3343
- return result;
3344
- }
3345
- /** Analyzes a single file using the appropriate language detector. */
3346
- async analyzeSingleFile(filePath, content) {
3347
- return this.analyzeFile(filePath, content);
3348
- }
3349
- /** Determines if a file should be analyzed based on language support. */
3350
- shouldAnalyzeFile(filePath, status) {
3351
- if (status === "removed") {
3352
- return false;
3353
- }
3354
- if (filePath.includes("/vendor/") || filePath.startsWith("vendor/")) {
3355
- return false;
3356
- }
3357
- if (filePath.includes("/node_modules/") || filePath.startsWith("node_modules/")) {
3358
- return false;
3359
- }
3360
- return !!this.registry.getDetectorForFile(filePath);
3361
- }
3362
- /** Returns all supported file extensions. */
3363
- getSupportedExtensions() {
3364
- return this.registry.getSupportedExtensions();
3365
- }
3366
- async analyzeFile(filePath, content) {
3367
- const result = {
3368
- filePath,
3369
- language: "",
3370
- flags: [],
3371
- parseErrors: [],
3372
- status: "ok"
3373
- };
3374
- if (!filePath) {
3375
- result.parseErrors.push(new Error("empty file path"));
3376
- result.status = "skipped";
3377
- result.skippedReason = "empty file path";
3378
- return result;
3379
- }
3380
- if (content.length === 0) {
3381
- return result;
3382
- }
3383
- if (content.length > MAX_FILE_SIZE) {
3384
- result.parseErrors.push(new Error(`file too large: ${content.length} bytes`));
3385
- result.status = "skipped";
3386
- result.skippedReason = `file too large (${Math.floor(content.length / (1024 * 1024))} MB)`;
3387
- return result;
3388
- }
3389
- const detector = this.registry.getDetectorForFile(filePath);
3390
- if (!detector) {
3391
- result.status = "unsupported";
3392
- return result;
3393
- }
3394
- result.language = detector.language();
3395
- try {
3396
- const flags = await Promise.resolve(detector.detectFlags(filePath, content));
3397
- result.flags = flags;
3398
- } catch (err) {
3399
- const error = err instanceof Error ? err : new Error(String(err));
3400
- result.parseErrors.push(error);
3401
- if (error.message.includes("operation limit")) {
3402
- result.status = "skipped";
3403
- result.skippedReason = "file too complex for parsing";
3404
- } else {
3405
- result.status = "partial";
3406
- }
3407
- }
3408
- return result;
3409
- }
3410
- };
3411
-
3412
- // src/detection/yaml-config.ts
3413
- import { z } from "zod";
3414
- var SupportedLanguageSchema = z.enum([
3415
- "go",
3416
- "typescript",
3417
- "javascript",
3418
- "python",
3419
- "java",
3420
- "kotlin",
3421
- "swift",
3422
- "ruby",
3423
- "csharp",
3424
- "php",
3425
- "rust",
3426
- "cpp",
3427
- "objc",
3428
- "all"
3429
- ]);
3430
- var ValidReturnTypes = ["boolean", "string", "integer", "float", "json"];
3431
- var MethodConfigSchema = z.object({
3432
- name: z.string().min(1, "name is required"),
3433
- flag_key_index: z.number().int().min(-1).default(0),
3434
- context_index: z.number().int().optional().default(0),
3435
- min_params: z.number().int().nonnegative().optional().default(0),
3436
- examples: z.array(z.string()).optional().default([]),
3437
- return_type: z.enum(["", ...ValidReturnTypes]).optional().default(""),
3438
- default_value_index: z.number().int().nonnegative().optional().default(0)
3439
- });
3440
- var FeatureFlagProviderSchema = z.object({
3441
- name: z.string().min(1, "name is required"),
3442
- languages: z.array(SupportedLanguageSchema).optional().default([]),
3443
- import_pattern: z.string().default(""),
3444
- methods: z.array(MethodConfigSchema).min(1, "at least one method must be configured"),
3445
- import_aliases: z.array(z.string()).optional().default([]),
3446
- description: z.string().optional().default(""),
3447
- enabled: z.boolean().default(true)
3448
- });
3449
- var GlobalConfigSchema = z.object({
3450
- enable_fallback_detection: z.boolean().default(false),
3451
- strict_import_matching: z.boolean().default(false),
3452
- custom_patterns: z.array(z.string()).optional().default([])
3453
- });
3454
- var FeatureFlagConfigSchema = z.object({
3455
- version: z.string().min(1, "version is required"),
3456
- providers: z.array(FeatureFlagProviderSchema).min(1, "at least one provider must be configured"),
3457
- global_settings: GlobalConfigSchema.optional().default({
3458
- enable_fallback_detection: false,
3459
- strict_import_matching: false,
3460
- custom_patterns: []
3461
- })
3462
- });
3463
-
3464
- // src/detection/index.ts
3465
- function createDefaultRegistry() {
3466
- const registry = new LanguageRegistry();
3467
- registry.register(new GoDetector());
3468
- registry.register(new TypeScriptDetector());
3469
- registry.register(new PythonDetector());
3470
- registry.register(new JavaDetector());
3471
- registry.register(new KotlinDetector());
3472
- registry.register(new SwiftDetector());
3473
- registry.register(new RubyDetector());
3474
- registry.register(new CSharpDetector());
3475
- registry.register(new PHPDetector());
3476
- registry.register(new RustDetector());
3477
- registry.register(new CPPDetector());
3478
- registry.register(new ObjectiveCDetector());
3479
- registry.register(new JavaScriptDetector());
3480
- return registry;
3481
- }
4
+ import { scanRepo } from "@flagshark/core";
3482
5
 
3483
6
  // src/formatter.ts
3484
- var VERSION = "1.0.0";
7
+ var VERSION = "1.1.0";
3485
8
  function pad(str, width) {
3486
9
  if (str.length > width) {
3487
10
  return str.slice(0, width - 1) + "\u2026";
@@ -3524,9 +47,8 @@ function formatText(result, options) {
3524
47
  const lines = [];
3525
48
  lines.push(`\u{1F988} FlagShark v${VERSION}`);
3526
49
  lines.push("");
3527
- const langCount = result.languageBreakdown.size;
3528
- const fileCount = Array.from(result.languageBreakdown.values()).reduce((sum, n) => sum + n, 0);
3529
- lines.push(`Scanned ${fileCount} files across ${langCount} language${langCount === 1 ? "" : "s"}`);
50
+ const langCount = Object.keys(result.languageBreakdown).length;
51
+ lines.push(`Scanned ${result.filesScanned} files across ${langCount} language${langCount === 1 ? "" : "s"}`);
3530
52
  if (result.totalFlags === 0) {
3531
53
  lines.push("No feature flags detected.");
3532
54
  lines.push("");
@@ -3560,15 +82,13 @@ function formatText(result, options) {
3560
82
  `Flag Health Score: ${result.healthScore}/100 (${staleCount}/${result.totalFlags} flags are stale)`
3561
83
  );
3562
84
  lines.push("");
3563
- lines.push("Full analysis \u2192 https://github.com/FlagShark/flagshark");
85
+ lines.push("Automate cleanup \u2192 https://flagshark.com");
86
+ lines.push("Open source CLI \u2192 https://github.com/FlagShark/flagshark");
3564
87
  }
3565
88
  return lines.join("\n");
3566
89
  }
3567
90
  function formatJson(result) {
3568
- const languages = {};
3569
- for (const [lang, count] of result.languageBreakdown) {
3570
- languages[lang] = count;
3571
- }
91
+ const languages = { ...result.languageBreakdown };
3572
92
  const flags = result.staleFlags.map((sf) => ({
3573
93
  name: sf.name,
3574
94
  file: sf.filePath,
@@ -3591,183 +111,17 @@ function formatJson(result) {
3591
111
  languages,
3592
112
  flags,
3593
113
  scanDuration: result.scanDuration,
3594
- url: "https://github.com/FlagShark/flagshark"
3595
- };
3596
- return JSON.stringify(output, null, 2);
3597
- }
3598
-
3599
- // src/staleness.ts
3600
- import { execFileSync, execSync } from "child_process";
3601
- function isShallowRepo(repoRoot) {
3602
- try {
3603
- const out = execSync("git rev-parse --is-shallow-repository", {
3604
- cwd: repoRoot,
3605
- encoding: "utf-8",
3606
- stdio: ["pipe", "pipe", "pipe"]
3607
- }).trim();
3608
- return out === "true";
3609
- } catch {
3610
- return false;
3611
- }
3612
- }
3613
- function parseBlamePortcelain(output) {
3614
- const lineAges = /* @__PURE__ */ new Map();
3615
- const lines = output.split("\n");
3616
- let currentLine = 0;
3617
- let currentAuthorTime = 0;
3618
- for (const line of lines) {
3619
- const headerMatch = line.match(/^[0-9a-f]{40}\s+\d+\s+(\d+)(?:\s+\d+)?$/);
3620
- if (headerMatch) {
3621
- currentLine = parseInt(headerMatch[1], 10);
3622
- continue;
3623
- }
3624
- if (line.startsWith("author-time ")) {
3625
- currentAuthorTime = parseInt(line.slice("author-time ".length), 10);
3626
- continue;
3627
- }
3628
- if (line.startsWith(" ")) {
3629
- if (currentLine > 0 && currentAuthorTime > 0) {
3630
- lineAges.set(currentLine, currentAuthorTime);
3631
- }
114
+ links: {
115
+ dashboard: "https://flagshark.com",
116
+ cli: "https://github.com/FlagShark/flagshark",
117
+ npm: "https://www.npmjs.com/package/flagshark"
3632
118
  }
3633
- }
3634
- return lineAges;
3635
- }
3636
- function blameFile(filePath, repoRoot) {
3637
- try {
3638
- const out = execFileSync("git", ["blame", "--porcelain", "--", filePath], {
3639
- cwd: repoRoot,
3640
- encoding: "utf-8",
3641
- stdio: ["pipe", "pipe", "pipe"],
3642
- maxBuffer: 10 * 1024 * 1024
3643
- // 10 MB – large files
3644
- });
3645
- return parseBlamePortcelain(out);
3646
- } catch {
3647
- return null;
3648
- }
3649
- }
3650
- function formatAge(unixSeconds) {
3651
- const nowMs = Date.now();
3652
- const thenMs = unixSeconds * 1e3;
3653
- const diffMs = nowMs - thenMs;
3654
- const seconds = Math.floor(diffMs / 1e3);
3655
- const minutes = Math.floor(seconds / 60);
3656
- const hours = Math.floor(minutes / 60);
3657
- const days = Math.floor(hours / 24);
3658
- const months = Math.floor(days / 30.44);
3659
- const years = Math.floor(days / 365.25);
3660
- if (years >= 1) {
3661
- return `${years} year${years === 1 ? "" : "s"} ago`;
3662
- }
3663
- if (months >= 1) {
3664
- return `${months} month${months === 1 ? "" : "s"} ago`;
3665
- }
3666
- if (days >= 1) {
3667
- return `${days} day${days === 1 ? "" : "s"} ago`;
3668
- }
3669
- return "less than a day ago";
3670
- }
3671
- function checkAgeSignal(authorTime, thresholdMonths) {
3672
- if (authorTime === void 0) {
3673
- return null;
3674
- }
3675
- const thresholdMs = thresholdMonths * 30.44 * 24 * 60 * 60 * 1e3;
3676
- const ageMs = Date.now() - authorTime * 1e3;
3677
- if (ageMs < thresholdMs) {
3678
- return null;
3679
- }
3680
- const age = formatAge(authorTime);
3681
- return {
3682
- signal: {
3683
- type: "age",
3684
- description: `Flag reference last modified ${age} (threshold: ${thresholdMonths} months)`
3685
- },
3686
- age
3687
- };
3688
- }
3689
- function checkLowUsageSignal(flagName, occurrences) {
3690
- const uniqueFiles = new Set(occurrences.map((o) => o.filePath));
3691
- if (uniqueFiles.size > 1) {
3692
- return null;
3693
- }
3694
- return {
3695
- type: "low-usage",
3696
- description: `Flag "${flagName}" only appears in 1 file \u2014 may have been fully rolled out`
3697
119
  };
3698
- }
3699
- function checkHardcodedSignal(_flag) {
3700
- return null;
3701
- }
3702
- async function analyzeStaleness(flags, options) {
3703
- const { thresholdMonths = 6, repoRoot } = options;
3704
- const shallow = isShallowRepo(repoRoot);
3705
- const fileBlames = /* @__PURE__ */ new Map();
3706
- if (!shallow) {
3707
- const filesToBlame = /* @__PURE__ */ new Set();
3708
- for (const occurrences of flags.values()) {
3709
- for (const flag of occurrences) {
3710
- filesToBlame.add(flag.filePath);
3711
- }
3712
- }
3713
- for (const file of filesToBlame) {
3714
- fileBlames.set(file, blameFile(file, repoRoot));
3715
- }
3716
- }
3717
- const staleFlags = [];
3718
- for (const [flagName, occurrences] of flags) {
3719
- const lowUsageSignal = checkLowUsageSignal(flagName, occurrences);
3720
- for (const flag of occurrences) {
3721
- const signals = [];
3722
- let age;
3723
- if (!shallow) {
3724
- const blame = fileBlames.get(flag.filePath);
3725
- const authorTime = blame?.get(flag.lineNumber);
3726
- const ageResult = checkAgeSignal(authorTime, thresholdMonths);
3727
- if (ageResult) {
3728
- signals.push(ageResult.signal);
3729
- age = ageResult.age;
3730
- } else if (authorTime !== void 0) {
3731
- age = formatAge(authorTime);
3732
- }
3733
- }
3734
- if (lowUsageSignal) {
3735
- signals.push(lowUsageSignal);
3736
- }
3737
- const hardcoded = checkHardcodedSignal(flag);
3738
- if (hardcoded) {
3739
- signals.push(hardcoded);
3740
- }
3741
- if (signals.length > 0) {
3742
- staleFlags.push({
3743
- name: flag.name,
3744
- filePath: flag.filePath,
3745
- lineNumber: flag.lineNumber,
3746
- language: flag.language,
3747
- provider: flag.provider ?? "unknown",
3748
- signals,
3749
- age
3750
- });
3751
- }
3752
- }
3753
- }
3754
- return staleFlags;
120
+ return JSON.stringify(output, null, 2);
3755
121
  }
3756
122
 
3757
123
  // src/cli.ts
3758
- var VERSION2 = "1.0.0";
3759
- var SKIP_DIRS = /* @__PURE__ */ new Set([
3760
- "node_modules",
3761
- "vendor",
3762
- ".git",
3763
- "dist",
3764
- "build",
3765
- "coverage",
3766
- "__pycache__",
3767
- ".next",
3768
- ".turbo"
3769
- ]);
3770
- var MAX_FILE_SIZE2 = 5 * 1024 * 1024;
124
+ var VERSION2 = "1.1.0";
3771
125
  var HELP_TEXT = `
3772
126
  flagshark scan [options]
3773
127
 
@@ -3841,89 +195,6 @@ function createLogger(verbose) {
3841
195
  error: (...args) => console.error("[error]", ...args)
3842
196
  };
3843
197
  }
3844
- function walkDirectory(dir, supportedExtensions, logger) {
3845
- const files = [];
3846
- function walk(currentDir) {
3847
- let entries;
3848
- try {
3849
- entries = fs.readdirSync(currentDir, { withFileTypes: true });
3850
- } catch {
3851
- logger.warn(`Cannot read directory: ${currentDir}`);
3852
- return;
3853
- }
3854
- for (const entry of entries) {
3855
- const fullPath = path.join(currentDir, entry.name);
3856
- if (entry.isDirectory()) {
3857
- if (SKIP_DIRS.has(entry.name)) {
3858
- continue;
3859
- }
3860
- walk(fullPath);
3861
- continue;
3862
- }
3863
- if (!entry.isFile()) {
3864
- continue;
3865
- }
3866
- const ext = path.extname(entry.name);
3867
- if (!supportedExtensions.has(ext)) {
3868
- continue;
3869
- }
3870
- try {
3871
- const stat = fs.statSync(fullPath);
3872
- if (stat.size > MAX_FILE_SIZE2) {
3873
- logger.debug(`Skipping large file: ${fullPath} (${stat.size} bytes)`);
3874
- continue;
3875
- }
3876
- } catch {
3877
- continue;
3878
- }
3879
- try {
3880
- const content = fs.readFileSync(fullPath, "utf-8");
3881
- files.push({ path: fullPath, content });
3882
- } catch {
3883
- logger.debug(`Cannot read file: ${fullPath}`);
3884
- }
3885
- }
3886
- }
3887
- walk(dir);
3888
- return files;
3889
- }
3890
- function getDiffFiles(ref, cwd, supportedExtensions, logger) {
3891
- let output;
3892
- try {
3893
- output = execFileSync2("git", ["diff", ref, "--name-only"], {
3894
- cwd,
3895
- encoding: "utf-8",
3896
- stdio: ["pipe", "pipe", "pipe"]
3897
- }).trim();
3898
- } catch (err) {
3899
- throw new Error(
3900
- `Failed to get diff against "${ref}": ${err instanceof Error ? err.message : String(err)}`
3901
- );
3902
- }
3903
- if (!output) {
3904
- return [];
3905
- }
3906
- const files = [];
3907
- for (const relPath of output.split("\n")) {
3908
- const ext = path.extname(relPath);
3909
- if (!supportedExtensions.has(ext)) {
3910
- continue;
3911
- }
3912
- const fullPath = path.resolve(cwd, relPath);
3913
- try {
3914
- const stat = fs.statSync(fullPath);
3915
- if (stat.size > MAX_FILE_SIZE2) {
3916
- logger.debug(`Skipping large file: ${fullPath}`);
3917
- continue;
3918
- }
3919
- const content = fs.readFileSync(fullPath, "utf-8");
3920
- files.push({ path: fullPath, content });
3921
- } catch {
3922
- logger.debug(`Cannot read changed file: ${fullPath}`);
3923
- }
3924
- }
3925
- return files;
3926
- }
3927
198
  async function main() {
3928
199
  const args = parseArgs(process.argv);
3929
200
  if (args.version) {
@@ -3935,62 +206,20 @@ async function main() {
3935
206
  process.stdout.write(HELP_TEXT + "\n");
3936
207
  process.exit(0);
3937
208
  }
3938
- const verbose = args.verbose;
3939
- const logger = createLogger(verbose);
3940
- const startTime = performance.now();
3941
- const cwd = process.cwd();
3942
- logger.debug("Creating language registry...");
3943
- const registry = createDefaultRegistry();
3944
- const analyzer = new PolyglotAnalyzer(registry, logger);
3945
- const supportedExtensions = new Set(registry.getSupportedExtensions());
3946
- logger.debug("Collecting files...");
3947
- let fileEntries;
209
+ const logger = createLogger(args.verbose);
3948
210
  if (args.diff) {
3949
211
  logger.info(`Scanning files changed since ${args.diff}...`);
3950
- fileEntries = getDiffFiles(args.diff, cwd, supportedExtensions, logger);
3951
212
  } else {
3952
213
  logger.info("Scanning current directory...");
3953
- fileEntries = walkDirectory(cwd, supportedExtensions, logger);
3954
- }
3955
- logger.debug(`Found ${fileEntries.length} files to scan`);
3956
- const fileMap = /* @__PURE__ */ new Map();
3957
- for (const entry of fileEntries) {
3958
- fileMap.set(entry.path, entry.content);
3959
- }
3960
- logger.debug("Running detection...");
3961
- const analysisResult = await analyzer.analyzeFiles(fileMap);
3962
- logger.debug("Analyzing staleness...");
3963
- const staleFlags = await analyzeStaleness(
3964
- analysisResult.totalFlags,
3965
- {
3966
- thresholdMonths: args.threshold,
3967
- repoRoot: cwd
3968
- }
3969
- );
3970
- const totalFlags = analysisResult.totalFlags.size;
3971
- const uniqueStaleNames = new Set(staleFlags.map((f) => f.name)).size;
3972
- const healthScore = totalFlags === 0 ? 100 : Math.round((totalFlags - uniqueStaleNames) / totalFlags * 100);
3973
- const allFlags = [];
3974
- for (const flags of analysisResult.totalFlags.values()) {
3975
- allFlags.push(...flags);
3976
214
  }
3977
- const detectedProviders = [
3978
- ...new Set(
3979
- allFlags.map((f) => f.provider).filter((p) => p !== null && p !== void 0 && p !== "")
3980
- )
3981
- ];
3982
- const languageBreakdown = analysisResult.languages;
3983
- const scanDuration = Math.round(performance.now() - startTime);
3984
- const result = {
3985
- totalFlags,
3986
- staleFlags,
3987
- detectedProviders,
3988
- languageBreakdown,
3989
- healthScore,
3990
- scanDuration
3991
- };
215
+ const result = await scanRepo({
216
+ cwd: process.cwd(),
217
+ threshold: args.threshold,
218
+ diff: args.diff ?? void 0,
219
+ logger
220
+ });
3992
221
  const output = args.json ? formatJson(result) + "\n" : formatText(result, { json: false, verbose: args.verbose, maxDisplay: 10 }) + "\n";
3993
- const exitCode = staleFlags.length > 0 ? 1 : 0;
222
+ const exitCode = result.staleFlags.length > 0 ? 1 : 0;
3994
223
  if (process.stdout.write(output)) {
3995
224
  process.exit(exitCode);
3996
225
  } else {