@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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,sBAAsB,CAAA;AAGpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjD,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAGlF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG/C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGjF,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,sBAAsB,CAAA;AAGpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjD,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAGlF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG/C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGjF,cAAc,mBAAmB,CAAA;AAGjC,cAAc,mBAAmB,CAAA;AAGjC,cAAc,sBAAsB,CAAA"}
package/dist/index.js CHANGED
@@ -8,4 +8,8 @@ export { collectFiles } from './scanner.js';
8
8
  export { scanRepo } from './scan-repo.js';
9
9
  // Config module
10
10
  export * from './config/index.js';
11
+ // Output formatters
12
+ export * from './output/index.js';
13
+ // Platform integration providers
14
+ export * from './providers/index.js';
11
15
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,cAAc,sBAAsB,CAAA;AAEpC,+BAA+B;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAGjD,gDAAgD;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAG3C,kCAAkC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAGzC,gBAAgB;AAChB,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,cAAc,sBAAsB,CAAA;AAEpC,+BAA+B;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAGjD,gDAAgD;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAG3C,kCAAkC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAGzC,gBAAgB;AAChB,cAAc,mBAAmB,CAAA;AAEjC,oBAAoB;AACpB,cAAc,mBAAmB,CAAA;AAEjC,iCAAiC;AACjC,cAAc,sBAAsB,CAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * RFC 4180-compliant CSV output. One row per stale flag.
3
+ *
4
+ * Header columns: flag, file, line, language, provider, signals, age
5
+ * Multi-value `signals` are joined with `; ` inside a single quoted cell.
6
+ */
7
+ import type { ScanRepoResult } from '../scan-repo.js';
8
+ export declare function formatCsv(result: ScanRepoResult): string;
9
+ //# sourceMappingURL=csv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/output/csv.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAUrD,wBAAgB,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAqBxD"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * RFC 4180-compliant CSV output. One row per stale flag.
3
+ *
4
+ * Header columns: flag, file, line, language, provider, signals, age
5
+ * Multi-value `signals` are joined with `; ` inside a single quoted cell.
6
+ */
7
+ const HEADER = 'flag,file,line,language,provider,signals,age,severity';
8
+ /** RFC 4180 cell escape: wrap in quotes; double any internal quotes. */
9
+ function csvCell(value) {
10
+ const s = value == null ? '' : String(value);
11
+ return `"${s.replace(/"/g, '""')}"`;
12
+ }
13
+ export function formatCsv(result) {
14
+ const lines = [HEADER];
15
+ for (const flag of result.staleFlags) {
16
+ const signals = flag.signals.map((s) => s.type).join('; ');
17
+ const severity = flag.signals.some((s) => s.severity === 'error') ? 'error' : 'warning';
18
+ lines.push([
19
+ csvCell(flag.name),
20
+ csvCell(flag.filePath),
21
+ String(flag.lineNumber),
22
+ csvCell(flag.language),
23
+ csvCell(flag.provider),
24
+ csvCell(signals),
25
+ csvCell(flag.age),
26
+ csvCell(severity),
27
+ ].join(','));
28
+ }
29
+ return lines.join('\n') + '\n';
30
+ }
31
+ //# sourceMappingURL=csv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.js","sourceRoot":"","sources":["../../src/output/csv.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,MAAM,GAAG,uDAAuD,CAAA;AAEtE,wEAAwE;AACxE,SAAS,OAAO,CAAC,KAAkC;IACjD,MAAM,CAAC,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC5C,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAsB;IAC9C,MAAM,KAAK,GAAa,CAAC,MAAM,CAAC,CAAA;IAEhC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;QACvF,KAAK,CAAC,IAAI,CACR;YACE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;YACtB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;YACvB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;YACtB,OAAO,CAAC,OAAO,CAAC;YAChB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;YACjB,OAAO,CAAC,QAAQ,CAAC;SAClB,CAAC,IAAI,CAAC,GAAG,CAAC,CACZ,CAAA;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAChC,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { formatText, type TextFormatOptions } from './text.js';
2
+ export { formatJson, type JsonFormatOptions } from './json.js';
3
+ export { formatMarkdown, type MarkdownFormatOptions } from './markdown.js';
4
+ export { formatCsv } from './csv.js';
5
+ export { formatSarif, type SarifFormatOptions } from './sarif.js';
6
+ export { selectFormatter, type FormatName, type Formatter, type UnifiedFormatOptions } from './select.js';
7
+ export { healthEmoji, uniqueStaleCount, sarifLevel } from './shared.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/output/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACzG,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,8 @@
1
+ export { formatText } from './text.js';
2
+ export { formatJson } from './json.js';
3
+ export { formatMarkdown } from './markdown.js';
4
+ export { formatCsv } from './csv.js';
5
+ export { formatSarif } from './sarif.js';
6
+ export { selectFormatter } from './select.js';
7
+ export { healthEmoji, uniqueStaleCount, sarifLevel } from './shared.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/output/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA0B,MAAM,WAAW,CAAA;AAC9D,OAAO,EAAE,UAAU,EAA0B,MAAM,WAAW,CAAA;AAC9D,OAAO,EAAE,cAAc,EAA8B,MAAM,eAAe,CAAA;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,WAAW,EAA2B,MAAM,YAAY,CAAA;AACjE,OAAO,EAAE,eAAe,EAA8D,MAAM,aAAa,CAAA;AACzG,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * JSON output for FlagShark scan results.
3
+ *
4
+ * This is the STABLE OUTPUT API — downstream tooling (the Action, custom CI
5
+ * scripts, the hosted SaaS) can rely on the shape produced here.
6
+ */
7
+ import type { ScanRepoResult } from '../scan-repo.js';
8
+ export interface JsonFormatOptions {
9
+ /** Library version to embed in the output (`version` field). */
10
+ version: string;
11
+ }
12
+ export declare function formatJson(result: ScanRepoResult, options: JsonFormatOptions): string;
13
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/output/json.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAuCrF"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * JSON output for FlagShark scan results.
3
+ *
4
+ * This is the STABLE OUTPUT API — downstream tooling (the Action, custom CI
5
+ * scripts, the hosted SaaS) can rely on the shape produced here.
6
+ */
7
+ export function formatJson(result, options) {
8
+ const languages = { ...result.languageBreakdown };
9
+ const flags = result.staleFlags.map((sf) => {
10
+ const flagSeverity = sf.signals.some((s) => s.severity === 'error') ? 'error' : 'warning';
11
+ return {
12
+ name: sf.name,
13
+ file: sf.filePath,
14
+ line: sf.lineNumber,
15
+ language: sf.language,
16
+ provider: sf.provider,
17
+ stale: true,
18
+ severity: flagSeverity,
19
+ signals: sf.signals.map((s) => ({ type: s.type, severity: s.severity, description: s.description })),
20
+ age: sf.age ?? null,
21
+ };
22
+ });
23
+ const errorCount = result.staleFlags.filter((sf) => sf.signals.some((s) => s.severity === 'error')).length;
24
+ const output = {
25
+ version: options.version,
26
+ totalFlags: result.totalFlags,
27
+ staleFlags: new Set(result.staleFlags.map((f) => f.name)).size,
28
+ errorCount,
29
+ healthScore: result.healthScore,
30
+ detectedProviders: result.detectedProviders,
31
+ languages,
32
+ flags,
33
+ excludedPaths: result.excludedPaths,
34
+ scanDuration: result.scanDuration,
35
+ links: {
36
+ dashboard: 'https://flagshark.com',
37
+ cli: 'https://github.com/FlagShark/flagshark',
38
+ npm: 'https://www.npmjs.com/package/flagshark',
39
+ },
40
+ };
41
+ return JSON.stringify(output, null, 2);
42
+ }
43
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/output/json.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,MAAM,UAAU,UAAU,CAAC,MAAsB,EAAE,OAA0B;IAC3E,MAAM,SAAS,GAA2B,EAAE,GAAG,MAAM,CAAC,iBAAiB,EAAE,CAAA;IAEzE,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACzC,MAAM,YAAY,GAAwB,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;QAC9G,OAAO;YACL,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,IAAI,EAAE,EAAE,CAAC,QAAQ;YACjB,IAAI,EAAE,EAAE,CAAC,UAAU;YACnB,QAAQ,EAAE,EAAE,CAAC,QAAQ;YACrB,QAAQ,EAAE,EAAE,CAAC,QAAQ;YACrB,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,YAAY;YACtB,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YACpG,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI;SACpB,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAA;IAE1G,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,UAAU,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QAC9D,UAAU;QACV,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,SAAS;QACT,KAAK;QACL,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,KAAK,EAAE;YACL,SAAS,EAAE,uBAAuB;YAClC,GAAG,EAAE,wCAAwC;YAC7C,GAAG,EAAE,yCAAyC;SAC/C;KACF,CAAA;IAED,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;AACxC,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Markdown output for FlagShark scan results.
3
+ *
4
+ * Used in two places:
5
+ * 1. CLI: `flagshark scan --format markdown > REPORT.md`
6
+ * 2. GitHub Action: PR comment body
7
+ *
8
+ * The Action passes `linkPrefix` so file paths render as absolute GitHub URLs;
9
+ * the CLI omits the prefix and uses relative paths.
10
+ */
11
+ import type { ScanRepoResult } from '../scan-repo.js';
12
+ export interface MarkdownFormatOptions {
13
+ /** 'full' or 'changed' — drives the "scan mode" label in the stats table. */
14
+ scanMode: 'full' | 'changed';
15
+ /** Prefix for file links (e.g. `https://github.com/owner/repo/blob/<sha>/`). When set, file paths become absolute URLs. */
16
+ linkPrefix?: string;
17
+ /** HTML comment to prepend (used by the Action to find + update its own comment). */
18
+ commentMarker?: string;
19
+ /** Cap on rendered stale flags before "...and N more". Default: 20. */
20
+ maxStaleFlags?: number;
21
+ }
22
+ export declare function formatMarkdown(result: ScanRepoResult, options: MarkdownFormatOptions): string;
23
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/output/markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAKrD,MAAM,WAAW,qBAAqB;IACpC,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B,2HAA2H;IAC3H,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,qFAAqF;IACrF,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAID,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAiF7F"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Markdown output for FlagShark scan results.
3
+ *
4
+ * Used in two places:
5
+ * 1. CLI: `flagshark scan --format markdown > REPORT.md`
6
+ * 2. GitHub Action: PR comment body
7
+ *
8
+ * The Action passes `linkPrefix` so file paths render as absolute GitHub URLs;
9
+ * the CLI omits the prefix and uses relative paths.
10
+ */
11
+ import { uniqueStaleCount, healthEmoji } from './shared.js';
12
+ const DEFAULT_MAX_STALE = 20;
13
+ export function formatMarkdown(result, options) {
14
+ const staleCount = uniqueStaleCount(result.staleFlags);
15
+ const emoji = healthEmoji(result.healthScore);
16
+ const modeLabel = options.scanMode === 'full' ? 'Full repo scan' : 'Changed files only';
17
+ const maxStale = options.maxStaleFlags ?? DEFAULT_MAX_STALE;
18
+ const langList = Object.entries(result.languageBreakdown)
19
+ .map(([l, c]) => `${l} (${c})`)
20
+ .join(', ');
21
+ const providerList = result.detectedProviders.length > 0
22
+ ? result.detectedProviders.slice(0, 5).join(', ') +
23
+ (result.detectedProviders.length > 5 ? ` +${result.detectedProviders.length - 5} more` : '')
24
+ : 'none detected';
25
+ let body = '';
26
+ if (options.commentMarker) {
27
+ body += `${options.commentMarker}\n`;
28
+ }
29
+ // Header
30
+ if (staleCount === 0) {
31
+ body += `## 🦈 FlagShark — All flags healthy\n\n`;
32
+ }
33
+ else {
34
+ body += `## 🦈 FlagShark — ${staleCount} stale flag${staleCount !== 1 ? 's' : ''} found\n\n`;
35
+ }
36
+ // Health badge
37
+ body += `${emoji} **Health Score: ${result.healthScore}/100**\n\n`;
38
+ // Compute error / warning split up front (used in both sections below)
39
+ const errorFlags = result.staleFlags.filter((f) => f.signals.some((s) => s.severity === 'error'));
40
+ const warningFlags = result.staleFlags.filter((f) => !f.signals.some((s) => s.severity === 'error'));
41
+ // Production-risk section (before stats table so it appears at a higher position)
42
+ if (errorFlags.length > 0) {
43
+ body += `### 🚨 Production-risk: flags missing in platform\n\n`;
44
+ body += '| Flag | File | Age | Why it looks stale |\n';
45
+ body += '|------|------|-----|--------------------|\n';
46
+ for (const flag of errorFlags) {
47
+ body += `| ${formatRow(flag, options.linkPrefix)} |\n`;
48
+ }
49
+ body += '\n';
50
+ }
51
+ // Stats table
52
+ body += `| Metric | Value |\n`;
53
+ body += `|--------|-------|\n`;
54
+ body += `| Flags detected | ${result.totalFlags} |\n`;
55
+ body += `| Stale flags | ${staleCount} |\n`;
56
+ body += `| Languages | ${langList || 'none'} |\n`;
57
+ body += `| Providers | ${providerList} |\n`;
58
+ body += `| Scan mode | ${modeLabel} |\n`;
59
+ body += `| Scan time | ${result.scanDuration}ms |\n\n`;
60
+ // Warning-severity stale flags section
61
+ if (warningFlags.length > 0) {
62
+ const displayFlags = warningFlags.slice(0, maxStale);
63
+ body += `<details${warningFlags.length <= 5 ? ' open' : ''}>\n`;
64
+ body += `<summary><strong>Stale flags (${warningFlags.length})</strong></summary>\n\n`;
65
+ body += '| Flag | File | Age | Why it looks stale |\n';
66
+ body += '|------|------|-----|--------------------|\n';
67
+ for (const flag of displayFlags) {
68
+ body += `| ${formatRow(flag, options.linkPrefix)} |\n`;
69
+ }
70
+ if (warningFlags.length > maxStale) {
71
+ body += `\n*... and ${warningFlags.length - maxStale} more. Run \`npx flagshark scan --verbose\` locally for the full list.*\n`;
72
+ }
73
+ body += '\n</details>\n\n';
74
+ }
75
+ // Footer
76
+ body += '---\n';
77
+ body += `*[FlagShark](https://github.com/FlagShark/flagshark) finds stale feature flags before they cause incidents*\n\n`;
78
+ body += `[Automate flag cleanup](https://flagshark.com) · `;
79
+ body += `[Install CLI](https://www.npmjs.com/package/flagshark) · `;
80
+ body += `[Open source](https://github.com/FlagShark/flagshark)\n`;
81
+ return body;
82
+ }
83
+ function formatRow(flag, linkPrefix) {
84
+ const signals = flag.signals.map((s) => s.description).join(', ');
85
+ const shortPath = flag.filePath.replace(/^\.\//, '');
86
+ const fileCell = linkPrefix
87
+ ? `[${shortPath}:${flag.lineNumber}](${normalizePrefix(linkPrefix)}${shortPath}#L${flag.lineNumber})`
88
+ : `\`${shortPath}:${flag.lineNumber}\``;
89
+ return `\`${flag.name}\` | ${fileCell} | ${flag.age || 'unknown'} | ${signals}`;
90
+ }
91
+ function normalizePrefix(prefix) {
92
+ return prefix.endsWith('/') ? prefix : prefix + '/';
93
+ }
94
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/output/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * SARIF v2.1.0 output for FlagShark scan results.
3
+ *
4
+ * Consumable directly by `github/codeql-action/upload-sarif` so stale flags
5
+ * appear in the repo's Security → Code Scanning tab — same UX as CodeQL,
6
+ * ESLint with SARIF output, etc.
7
+ *
8
+ * Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
9
+ */
10
+ import type { ScanRepoResult } from '../scan-repo.js';
11
+ export interface SarifFormatOptions {
12
+ /** Tool driver version (the FlagShark release version). */
13
+ version: string;
14
+ }
15
+ export declare function formatSarif(result: ScanRepoResult, options: SarifFormatOptions): string;
16
+ //# sourceMappingURL=sarif.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../../src/output/sarif.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAGrD,MAAM,WAAW,kBAAkB;IACjC,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAA;CAChB;AAwDD,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,kBAAkB,GAAG,MAAM,CA4BvF"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * SARIF v2.1.0 output for FlagShark scan results.
3
+ *
4
+ * Consumable directly by `github/codeql-action/upload-sarif` so stale flags
5
+ * appear in the repo's Security → Code Scanning tab — same UX as CodeQL,
6
+ * ESLint with SARIF output, etc.
7
+ *
8
+ * Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
9
+ */
10
+ const RULE_DEFS = {
11
+ 'stale-age': {
12
+ id: 'stale-age',
13
+ name: 'Stale by age',
14
+ shortDescription: { text: 'Flag reference older than the configured threshold' },
15
+ helpUri: 'https://github.com/FlagShark/flagshark#how-staleness-works',
16
+ },
17
+ 'stale-low-usage': {
18
+ id: 'stale-low-usage',
19
+ name: 'Stale by usage',
20
+ shortDescription: { text: 'Flag appears in only one file across the repo' },
21
+ helpUri: 'https://github.com/FlagShark/flagshark#how-staleness-works',
22
+ },
23
+ 'stale-hardcoded': {
24
+ id: 'stale-hardcoded',
25
+ name: 'Stale by hardcoded variation',
26
+ shortDescription: { text: 'Flag call uses a constant default — the flag may be permanently removed upstream' },
27
+ helpUri: 'https://github.com/FlagShark/flagshark#how-staleness-works',
28
+ },
29
+ 'flagshark/missing-in-platform': {
30
+ id: 'flagshark/missing-in-platform',
31
+ name: 'Missing in platform',
32
+ shortDescription: { text: 'Flag is referenced in code but does not exist in the feature flag platform' },
33
+ helpUri: 'https://github.com/FlagShark/flagshark#how-staleness-works',
34
+ },
35
+ 'flagshark/archived-in-platform': {
36
+ id: 'flagshark/archived-in-platform',
37
+ name: 'Archived in platform',
38
+ shortDescription: { text: 'Flag is referenced in code but has been archived in the feature flag platform' },
39
+ helpUri: 'https://github.com/FlagShark/flagshark#how-staleness-works',
40
+ },
41
+ };
42
+ function signalTypeToRuleId(signalType) {
43
+ if (signalType === 'age')
44
+ return 'stale-age';
45
+ if (signalType === 'low-usage')
46
+ return 'stale-low-usage';
47
+ if (signalType === 'missing-in-platform')
48
+ return 'flagshark/missing-in-platform';
49
+ if (signalType === 'archived-in-platform')
50
+ return 'flagshark/archived-in-platform';
51
+ return 'stale-hardcoded';
52
+ }
53
+ export function formatSarif(result, options) {
54
+ const results = result.staleFlags.map((flag) => toSarifResult(flag));
55
+ // Collect only the rule IDs that appear in results
56
+ const usedRuleIds = new Set(results.map((r) => r.ruleId));
57
+ const rules = [...usedRuleIds]
58
+ .filter((id) => id in RULE_DEFS)
59
+ .map((id) => RULE_DEFS[id]);
60
+ const envelope = {
61
+ $schema: 'https://json.schemastore.org/sarif-2.1.0.json',
62
+ version: '2.1.0',
63
+ runs: [
64
+ {
65
+ tool: {
66
+ driver: {
67
+ name: 'FlagShark',
68
+ version: options.version,
69
+ informationUri: 'https://github.com/FlagShark/flagshark',
70
+ rules,
71
+ },
72
+ },
73
+ results,
74
+ },
75
+ ],
76
+ };
77
+ return JSON.stringify(envelope, null, 2);
78
+ }
79
+ function toSarifResult(flag) {
80
+ const firstSignal = flag.signals[0];
81
+ const ruleId = signalTypeToRuleId(firstSignal ? firstSignal.type : '');
82
+ // Level is based on max severity across all signals
83
+ const level = flag.signals.some((s) => s.severity === 'error') ? 'error' : 'warning';
84
+ return {
85
+ ruleId,
86
+ level,
87
+ message: {
88
+ text: `Flag "${flag.name}" appears stale. ${flag.signals.map((s) => s.description).join('; ')}`,
89
+ },
90
+ locations: [{
91
+ physicalLocation: {
92
+ artifactLocation: { uri: flag.filePath.replace(/^\.\//, '') },
93
+ region: { startLine: flag.lineNumber },
94
+ },
95
+ }],
96
+ properties: {
97
+ flag: flag.name,
98
+ provider: flag.provider,
99
+ language: flag.language,
100
+ age: flag.age ?? '',
101
+ },
102
+ };
103
+ }
104
+ //# sourceMappingURL=sarif.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../src/output/sarif.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Format-name dispatcher. Returns a unified callable that all formatters
3
+ * conform to, so CLI/Action callers don't switch on format name themselves.
4
+ */
5
+ import type { ScanRepoResult } from '../scan-repo.js';
6
+ export type FormatName = 'text' | 'json' | 'markdown' | 'csv' | 'sarif';
7
+ export interface UnifiedFormatOptions {
8
+ /** Tool version (used by JSON + SARIF envelopes). */
9
+ version: string;
10
+ /** Scan mode label (used by markdown). */
11
+ scanMode: 'full' | 'changed';
12
+ /** Verbose flag (used by text). Default false. */
13
+ verbose?: boolean;
14
+ /** Max stale flags rendered in text/markdown. Default: 10 (text), 20 (markdown). */
15
+ maxDisplay?: number;
16
+ /** Link prefix for markdown (Action-context absolute URLs). */
17
+ linkPrefix?: string;
18
+ /** Comment marker (Action only). */
19
+ commentMarker?: string;
20
+ }
21
+ export type Formatter = (result: ScanRepoResult, options: UnifiedFormatOptions) => string;
22
+ export declare function selectFormatter(name: FormatName): Formatter;
23
+ //# sourceMappingURL=select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/output/select.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAQrD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,KAAK,GAAG,OAAO,CAAA;AAEvE,MAAM,WAAW,oBAAoB;IACnC,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,oBAAoB,KAAK,MAAM,CAAA;AAKzF,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,CAuB3D"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Format-name dispatcher. Returns a unified callable that all formatters
3
+ * conform to, so CLI/Action callers don't switch on format name themselves.
4
+ */
5
+ import { formatCsv } from './csv.js';
6
+ import { formatJson } from './json.js';
7
+ import { formatMarkdown } from './markdown.js';
8
+ import { formatSarif } from './sarif.js';
9
+ import { formatText } from './text.js';
10
+ const TEXT_DEFAULT_MAX = 10;
11
+ const MARKDOWN_DEFAULT_MAX = 20;
12
+ export function selectFormatter(name) {
13
+ switch (name) {
14
+ case 'text':
15
+ return (result, opts) => formatText(result, {
16
+ verbose: opts.verbose ?? false,
17
+ maxDisplay: opts.maxDisplay ?? TEXT_DEFAULT_MAX,
18
+ });
19
+ case 'json':
20
+ return (result, opts) => formatJson(result, { version: opts.version });
21
+ case 'markdown':
22
+ return (result, opts) => formatMarkdown(result, {
23
+ scanMode: opts.scanMode,
24
+ linkPrefix: opts.linkPrefix,
25
+ commentMarker: opts.commentMarker,
26
+ maxStaleFlags: opts.maxDisplay ?? MARKDOWN_DEFAULT_MAX,
27
+ });
28
+ case 'csv':
29
+ return (result) => formatCsv(result);
30
+ case 'sarif':
31
+ return (result, opts) => formatSarif(result, { version: opts.version });
32
+ default:
33
+ throw new Error(`Unknown format: ${name}`);
34
+ }
35
+ }
36
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/output/select.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAqBtC,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAC3B,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAE/B,MAAM,UAAU,eAAe,CAAC,IAAgB;IAC9C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE;gBAC1C,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK;gBAC9B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,gBAAgB;aAChD,CAAC,CAAA;QACJ,KAAK,MAAM;YACT,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;QACxE,KAAK,UAAU;YACb,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE;gBAC9C,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,aAAa,EAAE,IAAI,CAAC,UAAU,IAAI,oBAAoB;aACvD,CAAC,CAAA;QACJ,KAAK,KAAK;YACR,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QACtC,KAAK,OAAO;YACV,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;QACzE;YACE,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAc,EAAE,CAAC,CAAA;IACxD,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { StaleFlag } from '../staleness.js';
2
+ /** Returns the count of unique stale flag names (de-duped across occurrences). */
3
+ export declare function uniqueStaleCount(stale: StaleFlag[]): number;
4
+ /** Map health score to an emoji used in markdown + SARIF + Action summary. */
5
+ export declare function healthEmoji(score: number): string;
6
+ /**
7
+ * SARIF severity level mapping based on number of staleness signals on a flag.
8
+ * 1 signal → 'note'
9
+ * 2 signals → 'warning'
10
+ * 3+ signals → 'error'
11
+ */
12
+ export declare function sarifLevel(signalCount: number): 'note' | 'warning' | 'error';
13
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/output/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAE3D;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAI5E"}
@@ -0,0 +1,28 @@
1
+ /** Returns the count of unique stale flag names (de-duped across occurrences). */
2
+ export function uniqueStaleCount(stale) {
3
+ return new Set(stale.map((f) => f.name)).size;
4
+ }
5
+ /** Map health score to an emoji used in markdown + SARIF + Action summary. */
6
+ export function healthEmoji(score) {
7
+ if (score >= 90)
8
+ return '🟢';
9
+ if (score >= 70)
10
+ return '🟡';
11
+ if (score >= 40)
12
+ return '🟠';
13
+ return '🔴';
14
+ }
15
+ /**
16
+ * SARIF severity level mapping based on number of staleness signals on a flag.
17
+ * 1 signal → 'note'
18
+ * 2 signals → 'warning'
19
+ * 3+ signals → 'error'
20
+ */
21
+ export function sarifLevel(signalCount) {
22
+ if (signalCount >= 3)
23
+ return 'error';
24
+ if (signalCount === 2)
25
+ return 'warning';
26
+ return 'note';
27
+ }
28
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/output/shared.ts"],"names":[],"mappings":"AAEA,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,KAAkB;IACjD,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;AAC/C,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,IAAI,CAAA;IAC5B,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,IAAI,CAAA;IAC5B,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,IAAI,CAAA;IAC5B,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,WAAmB;IAC5C,IAAI,WAAW,IAAI,CAAC;QAAE,OAAO,OAAO,CAAA;IACpC,IAAI,WAAW,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IACvC,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Human-readable text output for FlagShark scan results.
3
+ */
4
+ import type { ScanRepoResult } from '../scan-repo.js';
5
+ export interface TextFormatOptions {
6
+ verbose: boolean;
7
+ /** Max stale flags to show before truncating. Default: 10. Ignored if verbose. */
8
+ maxDisplay: number;
9
+ }
10
+ export declare function formatText(result: ScanRepoResult, options: TextFormatOptions): string;
11
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/output/text.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAGrD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAA;CACnB;AAmDD,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAoErF"}