@flagshark/core 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +24 -0
  2. package/dist/config/excluder.d.ts.map +1 -1
  3. package/dist/config/excluder.js +1 -0
  4. package/dist/config/excluder.js.map +1 -1
  5. package/dist/config/loader.js +1 -0
  6. package/dist/config/loader.js.map +1 -1
  7. package/dist/config/schema.d.ts +25 -12
  8. package/dist/config/schema.d.ts.map +1 -1
  9. package/dist/config/schema.js +1 -0
  10. package/dist/config/schema.js.map +1 -1
  11. package/dist/detection/detectors/cpp.d.ts.map +1 -1
  12. package/dist/detection/detectors/cpp.js +6 -2
  13. package/dist/detection/detectors/cpp.js.map +1 -1
  14. package/dist/detection/detectors/javascript.d.ts.map +1 -1
  15. package/dist/detection/detectors/javascript.js +6 -2
  16. package/dist/detection/detectors/javascript.js.map +1 -1
  17. package/dist/detection/detectors/php.d.ts.map +1 -1
  18. package/dist/detection/detectors/php.js +6 -2
  19. package/dist/detection/detectors/php.js.map +1 -1
  20. package/dist/detection/detectors/python.d.ts.map +1 -1
  21. package/dist/detection/detectors/python.js +6 -2
  22. package/dist/detection/detectors/python.js.map +1 -1
  23. package/dist/detection/detectors/ruby.d.ts.map +1 -1
  24. package/dist/detection/detectors/ruby.js +7 -4
  25. package/dist/detection/detectors/ruby.js.map +1 -1
  26. package/dist/detection/detectors/typescript.d.ts.map +1 -1
  27. package/dist/detection/detectors/typescript.js +6 -2
  28. package/dist/detection/detectors/typescript.js.map +1 -1
  29. package/dist/detection/feature-flag.d.ts.map +1 -1
  30. package/dist/detection/helpers.d.ts.map +1 -1
  31. package/dist/detection/helpers.js +11 -1
  32. package/dist/detection/helpers.js.map +1 -1
  33. package/dist/detection/polyglot-analyzer.d.ts.map +1 -1
  34. package/dist/detection/polyglot-analyzer.js +50 -0
  35. package/dist/detection/polyglot-analyzer.js.map +1 -1
  36. package/dist/detection/tree-sitter/const-resolver.d.ts.map +1 -1
  37. package/dist/detection/tree-sitter/const-resolver.js +2 -2
  38. package/dist/detection/tree-sitter/const-resolver.js.map +1 -1
  39. package/dist/detection/tree-sitter/engine.js +1 -2
  40. package/dist/detection/tree-sitter/engine.js.map +1 -1
  41. package/dist/detection/tree-sitter/module-url.d.ts +25 -0
  42. package/dist/detection/tree-sitter/module-url.d.ts.map +1 -0
  43. package/dist/detection/tree-sitter/module-url.js +38 -0
  44. package/dist/detection/tree-sitter/module-url.js.map +1 -0
  45. package/dist/detection/tree-sitter/parser-cache.d.ts +4 -0
  46. package/dist/detection/tree-sitter/parser-cache.d.ts.map +1 -1
  47. package/dist/detection/tree-sitter/parser-cache.js +30 -6
  48. package/dist/detection/tree-sitter/parser-cache.js.map +1 -1
  49. package/dist/detection/tree-sitter/queries-inline.d.ts +13 -0
  50. package/dist/detection/tree-sitter/queries-inline.d.ts.map +1 -0
  51. package/dist/detection/tree-sitter/queries-inline.js +17 -0
  52. package/dist/detection/tree-sitter/queries-inline.js.map +1 -0
  53. package/dist/detection/tree-sitter/query-runner.d.ts +14 -1
  54. package/dist/detection/tree-sitter/query-runner.d.ts.map +1 -1
  55. package/dist/detection/tree-sitter/query-runner.js +28 -11
  56. package/dist/detection/tree-sitter/query-runner.js.map +1 -1
  57. package/dist/detection/yaml-config.js +2 -2
  58. package/dist/detection/yaml-config.js.map +1 -1
  59. package/dist/index.d.ts +2 -0
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +4 -0
  62. package/dist/index.js.map +1 -1
  63. package/dist/output/csv.d.ts +9 -0
  64. package/dist/output/csv.d.ts.map +1 -0
  65. package/dist/output/csv.js +31 -0
  66. package/dist/output/csv.js.map +1 -0
  67. package/dist/output/index.d.ts +8 -0
  68. package/dist/output/index.d.ts.map +1 -0
  69. package/dist/output/index.js +8 -0
  70. package/dist/output/index.js.map +1 -0
  71. package/dist/output/json.d.ts +13 -0
  72. package/dist/output/json.d.ts.map +1 -0
  73. package/dist/output/json.js +43 -0
  74. package/dist/output/json.js.map +1 -0
  75. package/dist/output/markdown.d.ts +23 -0
  76. package/dist/output/markdown.d.ts.map +1 -0
  77. package/dist/output/markdown.js +94 -0
  78. package/dist/output/markdown.js.map +1 -0
  79. package/dist/output/sarif.d.ts +16 -0
  80. package/dist/output/sarif.d.ts.map +1 -0
  81. package/dist/output/sarif.js +104 -0
  82. package/dist/output/sarif.js.map +1 -0
  83. package/dist/output/select.d.ts +23 -0
  84. package/dist/output/select.d.ts.map +1 -0
  85. package/dist/output/select.js +36 -0
  86. package/dist/output/select.js.map +1 -0
  87. package/dist/output/shared.d.ts +13 -0
  88. package/dist/output/shared.d.ts.map +1 -0
  89. package/dist/output/shared.js +28 -0
  90. package/dist/output/shared.js.map +1 -0
  91. package/dist/output/text.d.ts +11 -0
  92. package/dist/output/text.d.ts.map +1 -0
  93. package/dist/output/text.js +104 -0
  94. package/dist/output/text.js.map +1 -0
  95. package/dist/providers/cache.d.ts +28 -0
  96. package/dist/providers/cache.d.ts.map +1 -0
  97. package/dist/providers/cache.js +84 -0
  98. package/dist/providers/cache.js.map +1 -0
  99. package/dist/providers/cross-reference.d.ts +19 -0
  100. package/dist/providers/cross-reference.d.ts.map +1 -0
  101. package/dist/providers/cross-reference.js +48 -0
  102. package/dist/providers/cross-reference.js.map +1 -0
  103. package/dist/providers/index.d.ts +5 -0
  104. package/dist/providers/index.d.ts.map +1 -0
  105. package/dist/providers/index.js +4 -0
  106. package/dist/providers/index.js.map +1 -0
  107. package/dist/providers/interface.d.ts +37 -0
  108. package/dist/providers/interface.d.ts.map +1 -0
  109. package/dist/providers/interface.js +2 -0
  110. package/dist/providers/interface.js.map +1 -0
  111. package/dist/providers/launchdarkly/client.d.ts +13 -0
  112. package/dist/providers/launchdarkly/client.d.ts.map +1 -0
  113. package/dist/providers/launchdarkly/client.js +44 -0
  114. package/dist/providers/launchdarkly/client.js.map +1 -0
  115. package/dist/providers/launchdarkly/definition.d.ts +22 -0
  116. package/dist/providers/launchdarkly/definition.d.ts.map +1 -0
  117. package/dist/providers/launchdarkly/definition.js +24 -0
  118. package/dist/providers/launchdarkly/definition.js.map +1 -0
  119. package/dist/providers/launchdarkly/errors.d.ts +5 -0
  120. package/dist/providers/launchdarkly/errors.d.ts.map +1 -0
  121. package/dist/providers/launchdarkly/errors.js +9 -0
  122. package/dist/providers/launchdarkly/errors.js.map +1 -0
  123. package/dist/providers/launchdarkly/types.d.ts +154 -0
  124. package/dist/providers/launchdarkly/types.d.ts.map +1 -0
  125. package/dist/providers/launchdarkly/types.js +17 -0
  126. package/dist/providers/launchdarkly/types.js.map +1 -0
  127. package/dist/providers/orchestrate.d.ts +24 -0
  128. package/dist/providers/orchestrate.d.ts.map +1 -0
  129. package/dist/providers/orchestrate.js +44 -0
  130. package/dist/providers/orchestrate.js.map +1 -0
  131. package/dist/providers/registry.d.ts +8 -0
  132. package/dist/providers/registry.d.ts.map +1 -0
  133. package/dist/providers/registry.js +15 -0
  134. package/dist/providers/registry.js.map +1 -0
  135. package/dist/scan-repo.d.ts +2 -0
  136. package/dist/scan-repo.d.ts.map +1 -1
  137. package/dist/scan-repo.js +15 -6
  138. package/dist/scan-repo.js.map +1 -1
  139. package/dist/staleness.d.ts +4 -1
  140. package/dist/staleness.d.ts.map +1 -1
  141. package/dist/staleness.js +15 -11
  142. package/dist/staleness.js.map +1 -1
  143. package/package.json +6 -2
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Human-readable text output for FlagShark scan results.
3
+ */
4
+ function maxSeverity(signals) {
5
+ return signals.some((s) => s.severity === 'error') ? 'error' : 'warning';
6
+ }
7
+ function severityRank(s) {
8
+ return s === 'error' ? 0 : 1;
9
+ }
10
+ /** Pad a string to a fixed width, truncating with ellipsis if necessary. */
11
+ function pad(str, width) {
12
+ if (str.length > width) {
13
+ return str.slice(0, width - 1) + '…';
14
+ }
15
+ return str.padEnd(width);
16
+ }
17
+ function buildTable(flags) {
18
+ const cols = { flag: 16, file: 22, added: 13, signal: 28 };
19
+ const hBorder = (left, mid, right) => `${left}${'─'.repeat(cols.flag + 2)}${mid}${'─'.repeat(cols.file + 2)}${mid}${'─'.repeat(cols.added + 2)}${mid}${'─'.repeat(cols.signal + 2)}${right}`;
20
+ const lines = [];
21
+ lines.push(hBorder('┌', '┬', '┐'));
22
+ lines.push(`│ ${pad('Flag', cols.flag)} │ ${pad('File', cols.file)} │ ${pad('Added', cols.added)} │ ${pad('Signal', cols.signal)} │`);
23
+ lines.push(hBorder('├', '┼', '┤'));
24
+ for (const sf of flags) {
25
+ const fileRef = `${sf.filePath}:${sf.lineNumber}`;
26
+ const signalText = sf.signals
27
+ .map((s) => {
28
+ if (s.type === 'age')
29
+ return 'Age > threshold';
30
+ if (s.type === 'low-usage')
31
+ return 'Single file';
32
+ if (s.type === 'missing-in-platform')
33
+ return 'missing-in-platform';
34
+ if (s.type === 'archived-in-platform')
35
+ return 'archived-in-platform';
36
+ return s.description;
37
+ })
38
+ .join(', ');
39
+ lines.push(`│ ${pad(sf.name, cols.flag)} │ ${pad(fileRef, cols.file)} │ ${pad(sf.age ?? 'unknown', cols.added)} │ ${pad(signalText, cols.signal)} │`);
40
+ }
41
+ lines.push(hBorder('└', '┴', '┘'));
42
+ return lines.join('\n');
43
+ }
44
+ export function formatText(result, options) {
45
+ const lines = [];
46
+ lines.push(`\u{1F988} FlagShark`);
47
+ lines.push('');
48
+ const langCount = Object.keys(result.languageBreakdown).length;
49
+ lines.push(`Scanned ${result.filesScanned} files across ${langCount} language${langCount === 1 ? '' : 's'}`);
50
+ if (result.excludedCount && result.excludedCount > 0) {
51
+ lines.push(`(${result.excludedCount} excluded via .flagsharkignore + excludes)`);
52
+ }
53
+ if (result.totalFlags === 0) {
54
+ lines.push('No feature flags detected.');
55
+ lines.push('');
56
+ lines.push('Supported providers: LaunchDarkly, Unleash, Flipt, Split.io, PostHog, and more.');
57
+ lines.push('Run flagshark scan --help for configuration options.');
58
+ return lines.join('\n');
59
+ }
60
+ if (result.detectedProviders.length > 0) {
61
+ lines.push(`Detected providers: ${result.detectedProviders.join(', ')}`);
62
+ }
63
+ const uniqueStaleNames = new Set(result.staleFlags.map((f) => f.name));
64
+ const staleCount = uniqueStaleNames.size;
65
+ lines.push(`Found ${result.totalFlags} feature flags, ${staleCount} stale`);
66
+ if (staleCount > 0) {
67
+ lines.push('');
68
+ lines.push('Stale flags:');
69
+ const sorted = [...result.staleFlags].sort((a, b) => {
70
+ const sevA = severityRank(maxSeverity(a.signals));
71
+ const sevB = severityRank(maxSeverity(b.signals));
72
+ if (sevA !== sevB)
73
+ return sevA - sevB;
74
+ return 0;
75
+ });
76
+ const displayCount = options.verbose ? staleCount : Math.min(staleCount, options.maxDisplay);
77
+ const displayFlags = sorted.slice(0, displayCount);
78
+ lines.push(buildTable(displayFlags));
79
+ const remaining = staleCount - displayCount;
80
+ if (remaining > 0) {
81
+ lines.push('');
82
+ lines.push(`... and ${remaining} more (use --verbose to see all)`);
83
+ }
84
+ }
85
+ lines.push('');
86
+ if (staleCount === 0) {
87
+ lines.push(`Flag Health Score: ${result.healthScore}/100 ✓ All flags look healthy!`);
88
+ }
89
+ else {
90
+ lines.push(`Flag Health Score: ${result.healthScore}/100 (${staleCount}/${result.totalFlags} flags are stale)`);
91
+ lines.push('');
92
+ lines.push('Automate cleanup → https://flagshark.com');
93
+ lines.push('Open source CLI → https://github.com/FlagShark/flagshark');
94
+ }
95
+ if (result.excludedPaths && result.excludedPaths.length > 0) {
96
+ lines.push('');
97
+ lines.push(`Excluded files (${result.excludedPaths.length}):`);
98
+ for (const p of result.excludedPaths) {
99
+ lines.push(` ${p}`);
100
+ }
101
+ }
102
+ return lines.join('\n');
103
+ }
104
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/output/text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { PlatformClient, PlatformFlag } from './interface.js';
2
+ export interface CacheOptions {
3
+ /** Override the cache directory. Default: XDG-spec resolution. */
4
+ cacheDir?: string;
5
+ /** Time-to-live in milliseconds. Default: 24h. */
6
+ ttlMs?: number;
7
+ /** When true, skip the cache entirely. */
8
+ noCache?: boolean;
9
+ }
10
+ interface CacheReadResult {
11
+ fetchedAt: Date;
12
+ flags: PlatformFlag[];
13
+ }
14
+ /**
15
+ * Build a stable cache key from platform name + non-secret config + token hash.
16
+ * The raw token is never written to disk.
17
+ */
18
+ export declare function computeCacheKey(platformName: string, config: unknown, token: string): string;
19
+ /** Returns parsed cache contents, or null on miss / corruption / TTL expiry. */
20
+ export declare function readCache(key: string, opts?: CacheOptions): CacheReadResult | null;
21
+ /** Writes the cache file, creating directories as needed. Silent on error. */
22
+ export declare function writeCache(key: string, flags: PlatformFlag[], opts?: CacheOptions): void;
23
+ /** Reads from cache if fresh; otherwise calls the client and writes the result. */
24
+ export declare function loadPlatformFlagsCached(client: PlatformClient, cacheKey: string, opts?: CacheOptions & {
25
+ signal?: AbortSignal;
26
+ }): Promise<PlatformFlag[]>;
27
+ export {};
28
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/providers/cache.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAElE,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0CAA0C;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAOD,UAAU,eAAe;IACvB,SAAS,EAAE,IAAI,CAAA;IACf,KAAK,EAAE,YAAY,EAAE,CAAA;CACtB;AAWD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,GACZ,MAAM,CAIR;AAED,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,eAAe,GAAG,IAAI,CA4BtF;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,IAAI,GAAE,YAAiB,GAAG,IAAI,CAgB5F;AAED,mFAAmF;AACnF,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,YAAY,GAAG;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GACjD,OAAO,CAAC,YAAY,EAAE,CAAC,CAQzB"}
@@ -0,0 +1,84 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
6
+ function resolveCacheDir(override) {
7
+ if (override)
8
+ return override;
9
+ const xdg = process.env.XDG_CACHE_HOME;
10
+ const base = xdg && xdg.length > 0 ? xdg : join(homedir(), '.cache');
11
+ return join(base, 'flagshark');
12
+ }
13
+ /**
14
+ * Build a stable cache key from platform name + non-secret config + token hash.
15
+ * The raw token is never written to disk.
16
+ */
17
+ export function computeCacheKey(platformName, config, token) {
18
+ const tokenHash = createHash('sha256').update(token).digest('hex').slice(0, 8);
19
+ const configHash = createHash('sha256').update(JSON.stringify(config)).digest('hex').slice(0, 8);
20
+ return `v1-${platformName}-${configHash}-${tokenHash}`;
21
+ }
22
+ /** Returns parsed cache contents, or null on miss / corruption / TTL expiry. */
23
+ export function readCache(key, opts = {}) {
24
+ const dir = resolveCacheDir(opts.cacheDir);
25
+ const path = join(dir, `${key}.json`);
26
+ let raw;
27
+ try {
28
+ raw = readFileSync(path, 'utf-8');
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ let parsed;
34
+ try {
35
+ parsed = JSON.parse(raw);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ if (typeof parsed?.fetchedAt !== 'string' || !Array.isArray(parsed.flags)) {
41
+ return null;
42
+ }
43
+ const fetchedAt = new Date(parsed.fetchedAt);
44
+ const ttl = opts.ttlMs ?? DEFAULT_TTL_MS;
45
+ if (Date.now() - fetchedAt.getTime() >= ttl)
46
+ return null;
47
+ const flags = parsed.flags.map((f) => ({
48
+ key: f.key,
49
+ archived: f.archived,
50
+ lastModified: f.lastModified ? new Date(f.lastModified) : null,
51
+ }));
52
+ return { fetchedAt, flags };
53
+ }
54
+ /** Writes the cache file, creating directories as needed. Silent on error. */
55
+ export function writeCache(key, flags, opts = {}) {
56
+ const dir = resolveCacheDir(opts.cacheDir);
57
+ try {
58
+ mkdirSync(dir, { recursive: true });
59
+ const body = {
60
+ fetchedAt: new Date().toISOString(),
61
+ flags: flags.map((f) => ({
62
+ key: f.key,
63
+ archived: f.archived,
64
+ lastModified: f.lastModified ? f.lastModified.toISOString() : null,
65
+ })),
66
+ };
67
+ writeFileSync(join(dir, `${key}.json`), JSON.stringify(body));
68
+ }
69
+ catch {
70
+ // Cache write failure is non-fatal — data is still in memory for this run.
71
+ }
72
+ }
73
+ /** Reads from cache if fresh; otherwise calls the client and writes the result. */
74
+ export async function loadPlatformFlagsCached(client, cacheKey, opts = {}) {
75
+ if (!opts.noCache) {
76
+ const cached = readCache(cacheKey, opts);
77
+ if (cached)
78
+ return cached.flags;
79
+ }
80
+ const flags = await client.listFlags({ signal: opts.signal });
81
+ writeCache(cacheKey, flags, opts);
82
+ return flags;
83
+ }
84
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/providers/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { FeatureFlag } from '../detection/feature-flag.js';
2
+ import type { PlatformFlag, PlatformSignal } from './interface.js';
3
+ /**
4
+ * Pure function: joins detected flag keys against a platform's flag list,
5
+ * emits PlatformSignals for keys that are missing (error) or archived (warning).
6
+ *
7
+ * Does NOT surface platform flags with no code reference — that's a separate
8
+ * "orphan platform flags" feature, out of scope.
9
+ */
10
+ export declare function crossReference(detectedFlags: Map<string, FeatureFlag[]>, platformFlags: PlatformFlag[], platformDisplayName: string): Map<string, PlatformSignal[]>;
11
+ /**
12
+ * Merge platform signals from multiple platforms into a single per-flag map.
13
+ * If both LaunchDarkly and Unleash say a flag is missing, the flag's entry
14
+ * gets two signals (one per platform).
15
+ *
16
+ * Source arrays are cloned — subsequent mutations to source don't affect into.
17
+ */
18
+ export declare function mergePlatformSignals(into: Map<string, PlatformSignal[]>, source: Map<string, PlatformSignal[]>): void;
19
+ //# sourceMappingURL=cross-reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cross-reference.d.ts","sourceRoot":"","sources":["../../src/providers/cross-reference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAElE;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EACzC,aAAa,EAAE,YAAY,EAAE,EAC7B,mBAAmB,EAAE,MAAM,GAC1B,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAsB/B;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,EACnC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,GACpC,IAAI,CASN"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Pure function: joins detected flag keys against a platform's flag list,
3
+ * emits PlatformSignals for keys that are missing (error) or archived (warning).
4
+ *
5
+ * Does NOT surface platform flags with no code reference — that's a separate
6
+ * "orphan platform flags" feature, out of scope.
7
+ */
8
+ export function crossReference(detectedFlags, platformFlags, platformDisplayName) {
9
+ const platformByKey = new Map(platformFlags.map((f) => [f.key, f]));
10
+ const out = new Map();
11
+ for (const key of detectedFlags.keys()) {
12
+ const platform = platformByKey.get(key);
13
+ if (!platform) {
14
+ out.set(key, [{
15
+ type: 'missing-in-platform',
16
+ severity: 'error',
17
+ description: `referenced in code but not found in ${platformDisplayName}`,
18
+ }]);
19
+ }
20
+ else if (platform.archived) {
21
+ out.set(key, [{
22
+ type: 'archived-in-platform',
23
+ severity: 'warning',
24
+ description: `archived in ${platformDisplayName}`,
25
+ }]);
26
+ }
27
+ }
28
+ return out;
29
+ }
30
+ /**
31
+ * Merge platform signals from multiple platforms into a single per-flag map.
32
+ * If both LaunchDarkly and Unleash say a flag is missing, the flag's entry
33
+ * gets two signals (one per platform).
34
+ *
35
+ * Source arrays are cloned — subsequent mutations to source don't affect into.
36
+ */
37
+ export function mergePlatformSignals(into, source) {
38
+ for (const [key, signals] of source) {
39
+ const existing = into.get(key);
40
+ if (existing) {
41
+ existing.push(...signals);
42
+ }
43
+ else {
44
+ into.set(key, [...signals]);
45
+ }
46
+ }
47
+ }
48
+ //# sourceMappingURL=cross-reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cross-reference.js","sourceRoot":"","sources":["../../src/providers/cross-reference.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,aAAyC,EACzC,aAA6B,EAC7B,mBAA2B;IAE3B,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IACnE,MAAM,GAAG,GAAG,IAAI,GAAG,EAA4B,CAAA;IAE/C,KAAK,MAAM,GAAG,IAAI,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;oBACZ,IAAI,EAAE,qBAAqB;oBAC3B,QAAQ,EAAE,OAAO;oBACjB,WAAW,EAAE,uCAAuC,mBAAmB,EAAE;iBAC1E,CAAC,CAAC,CAAA;QACL,CAAC;aAAM,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YAC7B,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;oBACZ,IAAI,EAAE,sBAAsB;oBAC5B,QAAQ,EAAE,SAAS;oBACnB,WAAW,EAAE,eAAe,mBAAmB,EAAE;iBAClD,CAAC,CAAC,CAAA;QACL,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAmC,EACnC,MAAqC;IAErC,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,MAAM,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAA;QAC3B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type { PlatformFlag, PlatformClient, PlatformDefinition, PlatformSignal, } from './interface.js';
2
+ export { platformRegistry, findPlatform } from './registry.js';
3
+ export { crossReference, mergePlatformSignals } from './cross-reference.js';
4
+ export { computeCacheKey, readCache, writeCache, loadPlatformFlagsCached, type CacheOptions, } from './cache.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC3E,OAAO,EACL,eAAe,EACf,SAAS,EACT,UAAU,EACV,uBAAuB,EACvB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,4 @@
1
+ export { platformRegistry, findPlatform } from './registry.js';
2
+ export { crossReference, mergePlatformSignals } from './cross-reference.js';
3
+ export { computeCacheKey, readCache, writeCache, loadPlatformFlagsCached, } from './cache.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC3E,OAAO,EACL,eAAe,EACf,SAAS,EACT,UAAU,EACV,uBAAuB,GAExB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,37 @@
1
+ import type { ZodType } from 'zod';
2
+ /** A flag entry as reported by a flag-management platform's API. */
3
+ export interface PlatformFlag {
4
+ key: string;
5
+ /** Each platform maps its concept (archived/disabled/stale) to this boolean. */
6
+ archived: boolean;
7
+ lastModified: Date | null;
8
+ }
9
+ /** Runtime client for a configured platform. Returned by PlatformDefinition.createClient. */
10
+ export interface PlatformClient {
11
+ /** Registry key, e.g. 'launchdarkly'. */
12
+ name: string;
13
+ /** Human-readable name used in signal descriptions, e.g. 'LaunchDarkly'. */
14
+ displayName: string;
15
+ listFlags(opts?: {
16
+ signal?: AbortSignal;
17
+ }): Promise<PlatformFlag[]>;
18
+ }
19
+ /** Registry entry. Each platform implementation exports exactly one of these. */
20
+ export interface PlatformDefinition<TConfig = unknown> {
21
+ /** YAML key under `platforms:` and registry lookup key. */
22
+ name: string;
23
+ displayName: string;
24
+ /** Env var read for the secret token. User can override via token_env. */
25
+ defaultTokenEnv: string;
26
+ /** Zod schema validating this platform's config block. */
27
+ configSchema: ZodType<TConfig>;
28
+ /** Factory — validated config + resolved token → runtime client. No IO until listFlags() is called. */
29
+ createClient: (config: TConfig, token: string) => PlatformClient;
30
+ }
31
+ /** Signal type emitted by crossReference(). Merged into StaleFlag.signals[] by staleness.ts. */
32
+ export interface PlatformSignal {
33
+ type: 'missing-in-platform' | 'archived-in-platform';
34
+ severity: 'error' | 'warning';
35
+ description: string;
36
+ }
37
+ //# sourceMappingURL=interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/providers/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AAElC,oEAAoE;AACpE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,gFAAgF;IAChF,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,IAAI,GAAG,IAAI,CAAA;CAC1B;AAED,6FAA6F;AAC7F,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;CACpE;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAkB,CAAC,OAAO,GAAG,OAAO;IACnD,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,0EAA0E;IAC1E,eAAe,EAAE,MAAM,CAAA;IACvB,0DAA0D;IAC1D,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9B,uGAAuG;IACvG,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,cAAc,CAAA;CACjE;AAED,gGAAgG;AAChG,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,qBAAqB,GAAG,sBAAsB,CAAA;IACpD,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;CACpB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/providers/interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import type { PlatformFlag } from '../interface.js';
2
+ export interface FetchAllFlagsConfig {
3
+ project: string;
4
+ environment: string;
5
+ token: string;
6
+ }
7
+ export interface FetchAllFlagsOptions {
8
+ apiBase?: string;
9
+ fetch?: typeof globalThis.fetch;
10
+ signal?: AbortSignal;
11
+ }
12
+ export declare function fetchAllFlags(config: FetchAllFlagsConfig, opts?: FetchAllFlagsOptions): Promise<PlatformFlag[]>;
13
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/providers/launchdarkly/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAKnD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,EAC3B,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC,YAAY,EAAE,CAAC,CA+BzB"}
@@ -0,0 +1,44 @@
1
+ import { FlagsResponseSchema } from './types.js';
2
+ import { LdApiError } from './errors.js';
3
+ const DEFAULT_API_BASE = 'https://app.launchdarkly.com';
4
+ const LD_API_VERSION = '20240415';
5
+ export async function fetchAllFlags(config, opts = {}) {
6
+ const fetchFn = opts.fetch ?? globalThis.fetch;
7
+ const apiBase = opts.apiBase ?? DEFAULT_API_BASE;
8
+ const out = [];
9
+ let path = buildFirstPath(config.project, config.environment);
10
+ while (path) {
11
+ const res = await fetchFn(new URL(path, apiBase), {
12
+ headers: {
13
+ Authorization: config.token,
14
+ 'LD-API-Version': LD_API_VERSION,
15
+ },
16
+ signal: opts.signal,
17
+ });
18
+ if (!res.ok) {
19
+ throw new LdApiError(`LaunchDarkly API ${res.status} ${res.statusText}`, res.status);
20
+ }
21
+ const json = await res.json();
22
+ const parsed = FlagsResponseSchema.parse(json);
23
+ for (const item of parsed.items) {
24
+ const envData = item.environments?.[config.environment];
25
+ out.push({
26
+ key: item.key,
27
+ archived: item.archived,
28
+ lastModified: envData?.lastModified != null ? new Date(envData.lastModified) : null,
29
+ });
30
+ }
31
+ path = parsed._links?.next?.href;
32
+ }
33
+ return out;
34
+ }
35
+ function buildFirstPath(project, environment) {
36
+ const params = new URLSearchParams({
37
+ env: environment,
38
+ limit: '100',
39
+ offset: '0',
40
+ summary: '1',
41
+ });
42
+ return `/api/v2/flags/${encodeURIComponent(project)}?${params.toString()}`;
43
+ }
44
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/providers/launchdarkly/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxC,MAAM,gBAAgB,GAAG,8BAA8B,CAAA;AACvD,MAAM,cAAc,GAAG,UAAU,CAAA;AAcjC,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B,EAC3B,OAA6B,EAAE;IAE/B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAA;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,gBAAgB,CAAA;IAChD,MAAM,GAAG,GAAmB,EAAE,CAAA;IAC9B,IAAI,IAAI,GAAuB,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAA;IAEjF,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE;YAChD,OAAO,EAAE;gBACP,aAAa,EAAE,MAAM,CAAC,KAAK;gBAC3B,gBAAgB,EAAE,cAAc;aACjC;YACD,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAA;QACF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,UAAU,CAAC,oBAAoB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;QACtF,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;QAC7B,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC9C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;YACvD,GAAG,CAAC,IAAI,CAAC;gBACP,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,YAAY,EAAE,OAAO,EAAE,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;aACpF,CAAC,CAAA;QACJ,CAAC;QACD,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAA;IAClC,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,cAAc,CAAC,OAAe,EAAE,WAAmB;IAC1D,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,GAAG,EAAE,WAAW;QAChB,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,GAAG;KACb,CAAC,CAAA;IACF,OAAO,iBAAiB,kBAAkB,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAA;AAC5E,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod';
2
+ import type { PlatformDefinition } from '../interface.js';
3
+ declare const launchdarklyConfigSchema: z.ZodObject<{
4
+ project: z.ZodString;
5
+ environment: z.ZodString;
6
+ api_base: z.ZodOptional<z.ZodString>;
7
+ token_env: z.ZodOptional<z.ZodString>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ project: string;
10
+ environment: string;
11
+ token_env?: string | undefined;
12
+ api_base?: string | undefined;
13
+ }, {
14
+ project: string;
15
+ environment: string;
16
+ token_env?: string | undefined;
17
+ api_base?: string | undefined;
18
+ }>;
19
+ type LdConfig = z.infer<typeof launchdarklyConfigSchema>;
20
+ export declare const launchdarklyDefinition: PlatformDefinition<LdConfig>;
21
+ export {};
22
+ //# sourceMappingURL=definition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../../src/providers/launchdarkly/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAEzD,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;;EAK5B,CAAA;AAEF,KAAK,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAExD,eAAO,MAAM,sBAAsB,EAAE,kBAAkB,CAAC,QAAQ,CAc/D,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ import { fetchAllFlags } from './client.js';
3
+ const launchdarklyConfigSchema = z.object({
4
+ project: z.string(),
5
+ environment: z.string(),
6
+ api_base: z.string().url().optional(),
7
+ token_env: z.string().optional(),
8
+ });
9
+ export const launchdarklyDefinition = {
10
+ name: 'launchdarkly',
11
+ displayName: 'LaunchDarkly',
12
+ defaultTokenEnv: 'LAUNCHDARKLY_API_TOKEN',
13
+ configSchema: launchdarklyConfigSchema,
14
+ createClient: (cfg, token) => ({
15
+ name: 'launchdarkly',
16
+ displayName: 'LaunchDarkly',
17
+ listFlags: ({ signal } = {}) => fetchAllFlags({
18
+ project: cfg.project,
19
+ environment: cfg.environment,
20
+ token,
21
+ }, { apiBase: cfg.api_base, signal }),
22
+ }),
23
+ };
24
+ //# sourceMappingURL=definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.js","sourceRoot":"","sources":["../../../src/providers/launchdarkly/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3C,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAA;AAIF,MAAM,CAAC,MAAM,sBAAsB,GAAiC;IAClE,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE,cAAc;IAC3B,eAAe,EAAE,wBAAwB;IACzC,YAAY,EAAE,wBAAwB;IACtC,YAAY,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7B,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,cAAc;QAC3B,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC;YAC5C,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,KAAK;SACN,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;KACtC,CAAC;CACH,CAAA"}
@@ -0,0 +1,5 @@
1
+ export declare class LdApiError extends Error {
2
+ readonly status: number;
3
+ constructor(message: string, status: number);
4
+ }
5
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/providers/launchdarkly/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAW,SAAQ,KAAK;aACU,MAAM,EAAE,MAAM;gBAA/C,OAAO,EAAE,MAAM,EAAkB,MAAM,EAAE,MAAM;CAI5D"}
@@ -0,0 +1,9 @@
1
+ export class LdApiError extends Error {
2
+ status;
3
+ constructor(message, status) {
4
+ super(message);
5
+ this.status = status;
6
+ this.name = 'LdApiError';
7
+ }
8
+ }
9
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/providers/launchdarkly/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,UAAW,SAAQ,KAAK;IACU;IAA7C,YAAY,OAAe,EAAkB,MAAc;QACzD,KAAK,CAAC,OAAO,CAAC,CAAA;QAD6B,WAAM,GAAN,MAAM,CAAQ;QAEzD,IAAI,CAAC,IAAI,GAAG,YAAY,CAAA;IAC1B,CAAC;CACF"}