flagshark 1.0.1 → 1.1.1

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.1";
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("");
@@ -3566,10 +88,7 @@ function formatText(result, options) {
3566
88
  return lines.join("\n");
3567
89
  }
3568
90
  function formatJson(result) {
3569
- const languages = {};
3570
- for (const [lang, count] of result.languageBreakdown) {
3571
- languages[lang] = count;
3572
- }
91
+ const languages = { ...result.languageBreakdown };
3573
92
  const flags = result.staleFlags.map((sf) => ({
3574
93
  name: sf.name,
3575
94
  file: sf.filePath,
@@ -3601,178 +120,8 @@ function formatJson(result) {
3601
120
  return JSON.stringify(output, null, 2);
3602
121
  }
3603
122
 
3604
- // src/staleness.ts
3605
- import { execFileSync, execSync } from "child_process";
3606
- function isShallowRepo(repoRoot) {
3607
- try {
3608
- const out = execSync("git rev-parse --is-shallow-repository", {
3609
- cwd: repoRoot,
3610
- encoding: "utf-8",
3611
- stdio: ["pipe", "pipe", "pipe"]
3612
- }).trim();
3613
- return out === "true";
3614
- } catch {
3615
- return false;
3616
- }
3617
- }
3618
- function parseBlamePortcelain(output) {
3619
- const lineAges = /* @__PURE__ */ new Map();
3620
- const lines = output.split("\n");
3621
- let currentLine = 0;
3622
- let currentAuthorTime = 0;
3623
- for (const line of lines) {
3624
- const headerMatch = line.match(/^[0-9a-f]{40}\s+\d+\s+(\d+)(?:\s+\d+)?$/);
3625
- if (headerMatch) {
3626
- currentLine = parseInt(headerMatch[1], 10);
3627
- continue;
3628
- }
3629
- if (line.startsWith("author-time ")) {
3630
- currentAuthorTime = parseInt(line.slice("author-time ".length), 10);
3631
- continue;
3632
- }
3633
- if (line.startsWith(" ")) {
3634
- if (currentLine > 0 && currentAuthorTime > 0) {
3635
- lineAges.set(currentLine, currentAuthorTime);
3636
- }
3637
- }
3638
- }
3639
- return lineAges;
3640
- }
3641
- function blameFile(filePath, repoRoot) {
3642
- try {
3643
- const out = execFileSync("git", ["blame", "--porcelain", "--", filePath], {
3644
- cwd: repoRoot,
3645
- encoding: "utf-8",
3646
- stdio: ["pipe", "pipe", "pipe"],
3647
- maxBuffer: 10 * 1024 * 1024
3648
- // 10 MB – large files
3649
- });
3650
- return parseBlamePortcelain(out);
3651
- } catch {
3652
- return null;
3653
- }
3654
- }
3655
- function formatAge(unixSeconds) {
3656
- const nowMs = Date.now();
3657
- const thenMs = unixSeconds * 1e3;
3658
- const diffMs = nowMs - thenMs;
3659
- const seconds = Math.floor(diffMs / 1e3);
3660
- const minutes = Math.floor(seconds / 60);
3661
- const hours = Math.floor(minutes / 60);
3662
- const days = Math.floor(hours / 24);
3663
- const months = Math.floor(days / 30.44);
3664
- const years = Math.floor(days / 365.25);
3665
- if (years >= 1) {
3666
- return `${years} year${years === 1 ? "" : "s"} ago`;
3667
- }
3668
- if (months >= 1) {
3669
- return `${months} month${months === 1 ? "" : "s"} ago`;
3670
- }
3671
- if (days >= 1) {
3672
- return `${days} day${days === 1 ? "" : "s"} ago`;
3673
- }
3674
- return "less than a day ago";
3675
- }
3676
- function checkAgeSignal(authorTime, thresholdMonths) {
3677
- if (authorTime === void 0) {
3678
- return null;
3679
- }
3680
- const thresholdMs = thresholdMonths * 30.44 * 24 * 60 * 60 * 1e3;
3681
- const ageMs = Date.now() - authorTime * 1e3;
3682
- if (ageMs < thresholdMs) {
3683
- return null;
3684
- }
3685
- const age = formatAge(authorTime);
3686
- return {
3687
- signal: {
3688
- type: "age",
3689
- description: `Flag reference last modified ${age} (threshold: ${thresholdMonths} months)`
3690
- },
3691
- age
3692
- };
3693
- }
3694
- function checkLowUsageSignal(flagName, occurrences) {
3695
- const uniqueFiles = new Set(occurrences.map((o) => o.filePath));
3696
- if (uniqueFiles.size > 1) {
3697
- return null;
3698
- }
3699
- return {
3700
- type: "low-usage",
3701
- description: `Flag "${flagName}" only appears in 1 file \u2014 may have been fully rolled out`
3702
- };
3703
- }
3704
- function checkHardcodedSignal(_flag) {
3705
- return null;
3706
- }
3707
- async function analyzeStaleness(flags, options) {
3708
- const { thresholdMonths = 6, repoRoot } = options;
3709
- const shallow = isShallowRepo(repoRoot);
3710
- const fileBlames = /* @__PURE__ */ new Map();
3711
- if (!shallow) {
3712
- const filesToBlame = /* @__PURE__ */ new Set();
3713
- for (const occurrences of flags.values()) {
3714
- for (const flag of occurrences) {
3715
- filesToBlame.add(flag.filePath);
3716
- }
3717
- }
3718
- for (const file of filesToBlame) {
3719
- fileBlames.set(file, blameFile(file, repoRoot));
3720
- }
3721
- }
3722
- const staleFlags = [];
3723
- for (const [flagName, occurrences] of flags) {
3724
- const lowUsageSignal = checkLowUsageSignal(flagName, occurrences);
3725
- for (const flag of occurrences) {
3726
- const signals = [];
3727
- let age;
3728
- if (!shallow) {
3729
- const blame = fileBlames.get(flag.filePath);
3730
- const authorTime = blame?.get(flag.lineNumber);
3731
- const ageResult = checkAgeSignal(authorTime, thresholdMonths);
3732
- if (ageResult) {
3733
- signals.push(ageResult.signal);
3734
- age = ageResult.age;
3735
- } else if (authorTime !== void 0) {
3736
- age = formatAge(authorTime);
3737
- }
3738
- }
3739
- if (lowUsageSignal) {
3740
- signals.push(lowUsageSignal);
3741
- }
3742
- const hardcoded = checkHardcodedSignal(flag);
3743
- if (hardcoded) {
3744
- signals.push(hardcoded);
3745
- }
3746
- if (signals.length > 0) {
3747
- staleFlags.push({
3748
- name: flag.name,
3749
- filePath: flag.filePath,
3750
- lineNumber: flag.lineNumber,
3751
- language: flag.language,
3752
- provider: flag.provider ?? "unknown",
3753
- signals,
3754
- age
3755
- });
3756
- }
3757
- }
3758
- }
3759
- return staleFlags;
3760
- }
3761
-
3762
123
  // src/cli.ts
3763
- var VERSION2 = "1.0.0";
3764
- var SKIP_DIRS = /* @__PURE__ */ new Set([
3765
- "node_modules",
3766
- "vendor",
3767
- ".git",
3768
- "dist",
3769
- "build",
3770
- "coverage",
3771
- "__pycache__",
3772
- ".next",
3773
- ".turbo"
3774
- ]);
3775
- var MAX_FILE_SIZE2 = 5 * 1024 * 1024;
124
+ var VERSION2 = "1.1.1";
3776
125
  var HELP_TEXT = `
3777
126
  flagshark scan [options]
3778
127
 
@@ -3846,89 +195,6 @@ function createLogger(verbose) {
3846
195
  error: (...args) => console.error("[error]", ...args)
3847
196
  };
3848
197
  }
3849
- function walkDirectory(dir, supportedExtensions, logger) {
3850
- const files = [];
3851
- function walk(currentDir) {
3852
- let entries;
3853
- try {
3854
- entries = fs.readdirSync(currentDir, { withFileTypes: true });
3855
- } catch {
3856
- logger.warn(`Cannot read directory: ${currentDir}`);
3857
- return;
3858
- }
3859
- for (const entry of entries) {
3860
- const fullPath = path.join(currentDir, entry.name);
3861
- if (entry.isDirectory()) {
3862
- if (SKIP_DIRS.has(entry.name)) {
3863
- continue;
3864
- }
3865
- walk(fullPath);
3866
- continue;
3867
- }
3868
- if (!entry.isFile()) {
3869
- continue;
3870
- }
3871
- const ext = path.extname(entry.name);
3872
- if (!supportedExtensions.has(ext)) {
3873
- continue;
3874
- }
3875
- try {
3876
- const stat = fs.statSync(fullPath);
3877
- if (stat.size > MAX_FILE_SIZE2) {
3878
- logger.debug(`Skipping large file: ${fullPath} (${stat.size} bytes)`);
3879
- continue;
3880
- }
3881
- } catch {
3882
- continue;
3883
- }
3884
- try {
3885
- const content = fs.readFileSync(fullPath, "utf-8");
3886
- files.push({ path: fullPath, content });
3887
- } catch {
3888
- logger.debug(`Cannot read file: ${fullPath}`);
3889
- }
3890
- }
3891
- }
3892
- walk(dir);
3893
- return files;
3894
- }
3895
- function getDiffFiles(ref, cwd, supportedExtensions, logger) {
3896
- let output;
3897
- try {
3898
- output = execFileSync2("git", ["diff", ref, "--name-only"], {
3899
- cwd,
3900
- encoding: "utf-8",
3901
- stdio: ["pipe", "pipe", "pipe"]
3902
- }).trim();
3903
- } catch (err) {
3904
- throw new Error(
3905
- `Failed to get diff against "${ref}": ${err instanceof Error ? err.message : String(err)}`
3906
- );
3907
- }
3908
- if (!output) {
3909
- return [];
3910
- }
3911
- const files = [];
3912
- for (const relPath of output.split("\n")) {
3913
- const ext = path.extname(relPath);
3914
- if (!supportedExtensions.has(ext)) {
3915
- continue;
3916
- }
3917
- const fullPath = path.resolve(cwd, relPath);
3918
- try {
3919
- const stat = fs.statSync(fullPath);
3920
- if (stat.size > MAX_FILE_SIZE2) {
3921
- logger.debug(`Skipping large file: ${fullPath}`);
3922
- continue;
3923
- }
3924
- const content = fs.readFileSync(fullPath, "utf-8");
3925
- files.push({ path: fullPath, content });
3926
- } catch {
3927
- logger.debug(`Cannot read changed file: ${fullPath}`);
3928
- }
3929
- }
3930
- return files;
3931
- }
3932
198
  async function main() {
3933
199
  const args = parseArgs(process.argv);
3934
200
  if (args.version) {
@@ -3940,62 +206,20 @@ async function main() {
3940
206
  process.stdout.write(HELP_TEXT + "\n");
3941
207
  process.exit(0);
3942
208
  }
3943
- const verbose = args.verbose;
3944
- const logger = createLogger(verbose);
3945
- const startTime = performance.now();
3946
- const cwd = process.cwd();
3947
- logger.debug("Creating language registry...");
3948
- const registry = createDefaultRegistry();
3949
- const analyzer = new PolyglotAnalyzer(registry, logger);
3950
- const supportedExtensions = new Set(registry.getSupportedExtensions());
3951
- logger.debug("Collecting files...");
3952
- let fileEntries;
209
+ const logger = createLogger(args.verbose);
3953
210
  if (args.diff) {
3954
211
  logger.info(`Scanning files changed since ${args.diff}...`);
3955
- fileEntries = getDiffFiles(args.diff, cwd, supportedExtensions, logger);
3956
212
  } else {
3957
213
  logger.info("Scanning current directory...");
3958
- fileEntries = walkDirectory(cwd, supportedExtensions, logger);
3959
- }
3960
- logger.debug(`Found ${fileEntries.length} files to scan`);
3961
- const fileMap = /* @__PURE__ */ new Map();
3962
- for (const entry of fileEntries) {
3963
- fileMap.set(entry.path, entry.content);
3964
- }
3965
- logger.debug("Running detection...");
3966
- const analysisResult = await analyzer.analyzeFiles(fileMap);
3967
- logger.debug("Analyzing staleness...");
3968
- const staleFlags = await analyzeStaleness(
3969
- analysisResult.totalFlags,
3970
- {
3971
- thresholdMonths: args.threshold,
3972
- repoRoot: cwd
3973
- }
3974
- );
3975
- const totalFlags = analysisResult.totalFlags.size;
3976
- const uniqueStaleNames = new Set(staleFlags.map((f) => f.name)).size;
3977
- const healthScore = totalFlags === 0 ? 100 : Math.round((totalFlags - uniqueStaleNames) / totalFlags * 100);
3978
- const allFlags = [];
3979
- for (const flags of analysisResult.totalFlags.values()) {
3980
- allFlags.push(...flags);
3981
214
  }
3982
- const detectedProviders = [
3983
- ...new Set(
3984
- allFlags.map((f) => f.provider).filter((p) => p !== null && p !== void 0 && p !== "")
3985
- )
3986
- ];
3987
- const languageBreakdown = analysisResult.languages;
3988
- const scanDuration = Math.round(performance.now() - startTime);
3989
- const result = {
3990
- totalFlags,
3991
- staleFlags,
3992
- detectedProviders,
3993
- languageBreakdown,
3994
- healthScore,
3995
- scanDuration
3996
- };
215
+ const result = await scanRepo({
216
+ cwd: process.cwd(),
217
+ threshold: args.threshold,
218
+ diff: args.diff ?? void 0,
219
+ logger
220
+ });
3997
221
  const output = args.json ? formatJson(result) + "\n" : formatText(result, { json: false, verbose: args.verbose, maxDisplay: 10 }) + "\n";
3998
- const exitCode = staleFlags.length > 0 ? 1 : 0;
222
+ const exitCode = result.staleFlags.length > 0 ? 1 : 0;
3999
223
  if (process.stdout.write(output)) {
4000
224
  process.exit(exitCode);
4001
225
  } else {