@featurevisor/core 2.13.0 → 2.15.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 (121) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/coverage/clover.xml +1499 -1028
  3. package/coverage/coverage-final.json +21 -17
  4. package/coverage/lcov-report/builder/allocator.ts.html +1 -1
  5. package/coverage/lcov-report/builder/buildScopedConditions.ts.html +1 -1
  6. package/coverage/lcov-report/builder/buildScopedDatafile.ts.html +1 -1
  7. package/coverage/lcov-report/builder/buildScopedSegments.ts.html +1 -1
  8. package/coverage/lcov-report/builder/index.html +38 -8
  9. package/coverage/lcov-report/builder/mutateVariables.ts.html +400 -0
  10. package/coverage/lcov-report/builder/mutator.ts.html +796 -0
  11. package/coverage/lcov-report/builder/revision.ts.html +1 -1
  12. package/coverage/lcov-report/builder/traffic.ts.html +1 -1
  13. package/coverage/lcov-report/config/index.html +30 -15
  14. package/coverage/lcov-report/config/index.ts.html +88 -0
  15. package/coverage/lcov-report/config/projectConfig.ts.html +79 -46
  16. package/coverage/lcov-report/datasource/adapter.ts.html +74 -11
  17. package/coverage/lcov-report/datasource/datasource.ts.html +77 -26
  18. package/coverage/lcov-report/datasource/filesystemAdapter.ts.html +693 -69
  19. package/coverage/lcov-report/datasource/index.html +35 -35
  20. package/coverage/lcov-report/datasource/index.ts.html +1 -1
  21. package/coverage/lcov-report/index.html +53 -53
  22. package/coverage/lcov-report/linter/attributeSchema.ts.html +4 -4
  23. package/coverage/lcov-report/linter/checkCircularDependency.ts.html +1 -1
  24. package/coverage/lcov-report/linter/checkPercentageExceedingSlot.ts.html +1 -1
  25. package/coverage/lcov-report/linter/conditionSchema.ts.html +7 -7
  26. package/coverage/lcov-report/linter/featureSchema.ts.html +848 -389
  27. package/coverage/lcov-report/linter/groupSchema.ts.html +3 -3
  28. package/coverage/lcov-report/linter/index.html +50 -35
  29. package/coverage/lcov-report/linter/lintProject.ts.html +437 -92
  30. package/coverage/lcov-report/linter/mutationNotation.ts.html +1309 -0
  31. package/coverage/lcov-report/linter/printError.ts.html +9 -9
  32. package/coverage/lcov-report/linter/schema.ts.html +69 -69
  33. package/coverage/lcov-report/linter/segmentSchema.ts.html +3 -3
  34. package/coverage/lcov-report/linter/testSchema.ts.html +10 -10
  35. package/coverage/lcov-report/list/index.html +1 -1
  36. package/coverage/lcov-report/list/matrix.ts.html +1 -1
  37. package/coverage/lcov-report/parsers/index.html +1 -1
  38. package/coverage/lcov-report/parsers/index.ts.html +4 -4
  39. package/coverage/lcov-report/parsers/json.ts.html +2 -2
  40. package/coverage/lcov-report/parsers/yml.ts.html +6 -6
  41. package/coverage/lcov-report/tester/cliFormat.ts.html +1 -1
  42. package/coverage/lcov-report/tester/helpers.ts.html +1 -1
  43. package/coverage/lcov-report/tester/index.html +1 -1
  44. package/coverage/lcov-report/utils/git.ts.html +118 -16
  45. package/coverage/lcov-report/utils/index.html +14 -14
  46. package/coverage/lcov.info +2918 -1932
  47. package/lib/builder/buildDatafile.js +8 -3
  48. package/lib/builder/buildDatafile.js.map +1 -1
  49. package/lib/builder/mutateVariables.d.ts +14 -0
  50. package/lib/builder/mutateVariables.js +90 -0
  51. package/lib/builder/mutateVariables.js.map +1 -0
  52. package/lib/builder/mutateVariables.spec.d.ts +1 -0
  53. package/lib/builder/mutateVariables.spec.js +1045 -0
  54. package/lib/builder/mutateVariables.spec.js.map +1 -0
  55. package/lib/builder/mutator.d.ts +20 -0
  56. package/lib/builder/mutator.js +223 -0
  57. package/lib/builder/mutator.js.map +1 -0
  58. package/lib/builder/mutator.spec.d.ts +1 -0
  59. package/lib/builder/mutator.spec.js +368 -0
  60. package/lib/builder/mutator.spec.js.map +1 -0
  61. package/lib/config/projectConfig.d.ts +3 -0
  62. package/lib/config/projectConfig.js +7 -1
  63. package/lib/config/projectConfig.js.map +1 -1
  64. package/lib/config/projectConfig.spec.d.ts +1 -0
  65. package/lib/config/projectConfig.spec.js +24 -0
  66. package/lib/config/projectConfig.spec.js.map +1 -0
  67. package/lib/datasource/adapter.d.ts +7 -0
  68. package/lib/datasource/adapter.js +7 -0
  69. package/lib/datasource/adapter.js.map +1 -1
  70. package/lib/datasource/datasource.d.ts +3 -1
  71. package/lib/datasource/datasource.js +6 -0
  72. package/lib/datasource/datasource.js.map +1 -1
  73. package/lib/datasource/filesystemAdapter.d.ts +8 -0
  74. package/lib/datasource/filesystemAdapter.js +134 -7
  75. package/lib/datasource/filesystemAdapter.js.map +1 -1
  76. package/lib/datasource/filesystemAdapter.spec.d.ts +1 -0
  77. package/lib/datasource/filesystemAdapter.spec.js +88 -0
  78. package/lib/datasource/filesystemAdapter.spec.js.map +1 -0
  79. package/lib/linter/conditionSchema.spec.js +2 -0
  80. package/lib/linter/conditionSchema.spec.js.map +1 -1
  81. package/lib/linter/featureSchema.d.ts +7 -7
  82. package/lib/linter/featureSchema.js +176 -103
  83. package/lib/linter/featureSchema.js.map +1 -1
  84. package/lib/linter/featureSchema.spec.js +167 -0
  85. package/lib/linter/featureSchema.spec.js.map +1 -1
  86. package/lib/linter/lintProject.d.ts +2 -0
  87. package/lib/linter/lintProject.js +85 -8
  88. package/lib/linter/lintProject.js.map +1 -1
  89. package/lib/linter/lintProject.spec.js +37 -0
  90. package/lib/linter/lintProject.spec.js.map +1 -1
  91. package/lib/linter/mutationNotation.d.ts +47 -0
  92. package/lib/linter/mutationNotation.js +381 -0
  93. package/lib/linter/mutationNotation.js.map +1 -0
  94. package/lib/linter/mutationNotation.spec.d.ts +1 -0
  95. package/lib/linter/mutationNotation.spec.js +549 -0
  96. package/lib/linter/mutationNotation.spec.js.map +1 -0
  97. package/lib/linter/segmentSchema.spec.js +2 -0
  98. package/lib/linter/segmentSchema.spec.js.map +1 -1
  99. package/lib/utils/git.js +32 -7
  100. package/lib/utils/git.js.map +1 -1
  101. package/package.json +2 -2
  102. package/src/builder/buildDatafile.ts +29 -3
  103. package/src/builder/mutateVariables.spec.ts +1134 -0
  104. package/src/builder/mutateVariables.ts +105 -0
  105. package/src/builder/mutator.spec.ts +413 -0
  106. package/src/builder/mutator.ts +237 -0
  107. package/src/config/projectConfig.spec.ts +31 -0
  108. package/src/config/projectConfig.ts +11 -0
  109. package/src/datasource/adapter.ts +21 -0
  110. package/src/datasource/datasource.ts +18 -1
  111. package/src/datasource/filesystemAdapter.spec.ts +153 -0
  112. package/src/datasource/filesystemAdapter.ts +216 -8
  113. package/src/linter/conditionSchema.spec.ts +2 -0
  114. package/src/linter/featureSchema.spec.ts +203 -0
  115. package/src/linter/featureSchema.ts +320 -167
  116. package/src/linter/lintProject.spec.ts +74 -0
  117. package/src/linter/lintProject.ts +123 -8
  118. package/src/linter/mutationNotation.spec.ts +642 -0
  119. package/src/linter/mutationNotation.ts +408 -0
  120. package/src/linter/segmentSchema.spec.ts +2 -0
  121. package/src/utils/git.ts +41 -7
@@ -136,7 +136,7 @@
136
136
  <div class='footer quiet pad2 space-top1 center small'>
137
137
  Code coverage generated by
138
138
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
139
- at 2026-02-23T20:40:14.947Z
139
+ at 2026-02-25T22:03:15.189Z
140
140
  </div>
141
141
  <script src="../prettify.js"></script>
142
142
  <script>
@@ -643,7 +643,7 @@ export function getTraffic(
643
643
  <div class='footer quiet pad2 space-top1 center small'>
644
644
  Code coverage generated by
645
645
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
646
- at 2026-02-23T20:40:14.947Z
646
+ at 2026-02-25T22:03:15.189Z
647
647
  </div>
648
648
  <script src="../prettify.js"></script>
649
649
  <script>
@@ -23,16 +23,16 @@
23
23
  <div class='clearfix'>
24
24
 
25
25
  <div class='fl pad1y space-right2'>
26
- <span class="strong">70.9% </span>
26
+ <span class="strong">72.88% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>39/55</span>
28
+ <span class='fraction'>43/59</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
- <span class="strong">35.71% </span>
33
+ <span class="strong">47.05% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>5/14</span>
35
+ <span class='fraction'>8/17</span>
36
36
  </div>
37
37
 
38
38
 
@@ -44,9 +44,9 @@
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">72.22% </span>
47
+ <span class="strong">74.13% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>39/54</span>
49
+ <span class='fraction'>43/58</span>
50
50
  </div>
51
51
 
52
52
 
@@ -79,18 +79,33 @@
79
79
  </tr>
80
80
  </thead>
81
81
  <tbody><tr>
82
+ <td class="file high" data-value="index.ts"><a href="index.ts.html">index.ts</a></td>
83
+ <td data-value="100" class="pic high">
84
+ <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
85
+ </td>
86
+ <td data-value="100" class="pct high">100%</td>
87
+ <td data-value="1" class="abs high">1/1</td>
88
+ <td data-value="100" class="pct high">100%</td>
89
+ <td data-value="0" class="abs high">0/0</td>
90
+ <td data-value="100" class="pct high">100%</td>
91
+ <td data-value="0" class="abs high">0/0</td>
92
+ <td data-value="100" class="pct high">100%</td>
93
+ <td data-value="1" class="abs high">1/1</td>
94
+ </tr>
95
+
96
+ <tr>
82
97
  <td class="file medium" data-value="projectConfig.ts"><a href="projectConfig.ts.html">projectConfig.ts</a></td>
83
- <td data-value="70.9" class="pic medium">
84
- <div class="chart"><div class="cover-fill" style="width: 70%"></div><div class="cover-empty" style="width: 30%"></div></div>
98
+ <td data-value="72.41" class="pic medium">
99
+ <div class="chart"><div class="cover-fill" style="width: 72%"></div><div class="cover-empty" style="width: 28%"></div></div>
85
100
  </td>
86
- <td data-value="70.9" class="pct medium">70.9%</td>
87
- <td data-value="55" class="abs medium">39/55</td>
88
- <td data-value="35.71" class="pct low">35.71%</td>
89
- <td data-value="14" class="abs low">5/14</td>
101
+ <td data-value="72.41" class="pct medium">72.41%</td>
102
+ <td data-value="58" class="abs medium">42/58</td>
103
+ <td data-value="47.05" class="pct low">47.05%</td>
104
+ <td data-value="17" class="abs low">8/17</td>
90
105
  <td data-value="40" class="pct low">40%</td>
91
106
  <td data-value="5" class="abs low">2/5</td>
92
- <td data-value="72.22" class="pct medium">72.22%</td>
93
- <td data-value="54" class="abs medium">39/54</td>
107
+ <td data-value="73.68" class="pct medium">73.68%</td>
108
+ <td data-value="57" class="abs medium">42/57</td>
94
109
  </tr>
95
110
 
96
111
  </tbody>
@@ -101,7 +116,7 @@
101
116
  <div class='footer quiet pad2 space-top1 center small'>
102
117
  Code coverage generated by
103
118
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
104
- at 2026-02-23T20:40:14.947Z
119
+ at 2026-02-25T22:03:15.189Z
105
120
  </div>
106
121
  <script src="../prettify.js"></script>
107
122
  <script>
@@ -0,0 +1,88 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for config/index.ts</title>
7
+ <meta charset="utf-8" />
8
+ <link rel="stylesheet" href="../prettify.css" />
9
+ <link rel="stylesheet" href="../base.css" />
10
+ <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <style type='text/css'>
13
+ .coverage-summary .sorter {
14
+ background-image: url(../sort-arrow-sprite.png);
15
+ }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <div class='wrapper'>
21
+ <div class='pad1'>
22
+ <h1><a href="../index.html">All files</a> / <a href="index.html">config</a> index.ts</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">100% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>1/1</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">100% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>0/0</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">100% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>0/0</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">100% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>1/1</span>
50
+ </div>
51
+
52
+
53
+ </div>
54
+ <p class="quiet">
55
+ Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
+ </p>
57
+ <template id="filterTemplate">
58
+ <div class="quiet">
59
+ Filter:
60
+ <input oninput="onInput()" type="search" id="fileSearch">
61
+ </div>
62
+ </template>
63
+ </div>
64
+ <div class='status-line high'></div>
65
+ <pre><table class="coverage">
66
+ <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
+ <a name='L2'></a><a href='#L2'>2</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
68
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">export * from "./projectConfig";
69
+ &nbsp;</pre></td></tr></table></pre>
70
+
71
+ <div class='push'></div><!-- for sticky footer -->
72
+ </div><!-- /wrapper -->
73
+ <div class='footer quiet pad2 space-top1 center small'>
74
+ Code coverage generated by
75
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
76
+ at 2026-02-25T22:03:15.189Z
77
+ </div>
78
+ <script src="../prettify.js"></script>
79
+ <script>
80
+ window.onload = function () {
81
+ prettyPrint();
82
+ };
83
+ </script>
84
+ <script src="../sorter.js"></script>
85
+ <script src="../block-navigation.js"></script>
86
+ </body>
87
+ </html>
88
+
@@ -23,16 +23,16 @@
23
23
  <div class='clearfix'>
24
24
 
25
25
  <div class='fl pad1y space-right2'>
26
- <span class="strong">70.9% </span>
26
+ <span class="strong">72.41% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>39/55</span>
28
+ <span class='fraction'>42/58</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
- <span class="strong">35.71% </span>
33
+ <span class="strong">47.05% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>5/14</span>
35
+ <span class='fraction'>8/17</span>
36
36
  </div>
37
37
 
38
38
 
@@ -44,9 +44,9 @@
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">72.22% </span>
47
+ <span class="strong">73.68% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>39/54</span>
49
+ <span class='fraction'>42/57</span>
50
50
  </div>
51
51
 
52
52
 
@@ -260,40 +260,53 @@
260
260
  <a name='L195'></a><a href='#L195'>195</a>
261
261
  <a name='L196'></a><a href='#L196'>196</a>
262
262
  <a name='L197'></a><a href='#L197'>197</a>
263
- <a name='L198'></a><a href='#L198'>198</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
263
+ <a name='L198'></a><a href='#L198'>198</a>
264
+ <a name='L199'></a><a href='#L199'>199</a>
265
+ <a name='L200'></a><a href='#L200'>200</a>
266
+ <a name='L201'></a><a href='#L201'>201</a>
267
+ <a name='L202'></a><a href='#L202'>202</a>
268
+ <a name='L203'></a><a href='#L203'>203</a>
269
+ <a name='L204'></a><a href='#L204'>204</a>
270
+ <a name='L205'></a><a href='#L205'>205</a>
271
+ <a name='L206'></a><a href='#L206'>206</a>
272
+ <a name='L207'></a><a href='#L207'>207</a>
273
+ <a name='L208'></a><a href='#L208'>208</a>
274
+ <a name='L209'></a><a href='#L209'>209</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">3x</span>
264
275
  <span class="cline-any cline-neutral">&nbsp;</span>
265
276
  <span class="cline-any cline-neutral">&nbsp;</span>
266
277
  <span class="cline-any cline-neutral">&nbsp;</span>
267
- <span class="cline-any cline-yes">1x</span>
268
- <span class="cline-any cline-yes">1x</span>
278
+ <span class="cline-any cline-yes">3x</span>
279
+ <span class="cline-any cline-yes">3x</span>
269
280
  <span class="cline-any cline-neutral">&nbsp;</span>
270
281
  <span class="cline-any cline-neutral">&nbsp;</span>
271
282
  <span class="cline-any cline-neutral">&nbsp;</span>
272
- <span class="cline-any cline-yes">1x</span>
273
- <span class="cline-any cline-yes">1x</span>
274
- <span class="cline-any cline-yes">1x</span>
275
- <span class="cline-any cline-yes">1x</span>
276
- <span class="cline-any cline-yes">1x</span>
277
- <span class="cline-any cline-yes">1x</span>
278
- <span class="cline-any cline-yes">1x</span>
279
- <span class="cline-any cline-yes">1x</span>
280
- <span class="cline-any cline-yes">1x</span>
281
- <span class="cline-any cline-yes">1x</span>
282
- <span class="cline-any cline-yes">1x</span>
283
+ <span class="cline-any cline-yes">3x</span>
284
+ <span class="cline-any cline-yes">3x</span>
285
+ <span class="cline-any cline-yes">3x</span>
286
+ <span class="cline-any cline-yes">3x</span>
287
+ <span class="cline-any cline-yes">3x</span>
288
+ <span class="cline-any cline-yes">3x</span>
289
+ <span class="cline-any cline-yes">3x</span>
290
+ <span class="cline-any cline-yes">3x</span>
291
+ <span class="cline-any cline-yes">3x</span>
292
+ <span class="cline-any cline-yes">3x</span>
293
+ <span class="cline-any cline-yes">3x</span>
294
+ <span class="cline-any cline-yes">3x</span>
283
295
  <span class="cline-any cline-neutral">&nbsp;</span>
284
- <span class="cline-any cline-yes">1x</span>
285
- <span class="cline-any cline-yes">1x</span>
296
+ <span class="cline-any cline-yes">3x</span>
297
+ <span class="cline-any cline-yes">3x</span>
286
298
  <span class="cline-any cline-neutral">&nbsp;</span>
287
- <span class="cline-any cline-yes">1x</span>
288
- <span class="cline-any cline-yes">1x</span>
289
- <span class="cline-any cline-yes">1x</span>
299
+ <span class="cline-any cline-yes">3x</span>
300
+ <span class="cline-any cline-yes">3x</span>
301
+ <span class="cline-any cline-yes">3x</span>
290
302
  <span class="cline-any cline-neutral">&nbsp;</span>
291
- <span class="cline-any cline-yes">1x</span>
292
- <span class="cline-any cline-yes">1x</span>
303
+ <span class="cline-any cline-yes">3x</span>
304
+ <span class="cline-any cline-yes">3x</span>
293
305
  <span class="cline-any cline-neutral">&nbsp;</span>
294
- <span class="cline-any cline-yes">1x</span>
306
+ <span class="cline-any cline-yes">3x</span>
307
+ <span class="cline-any cline-neutral">&nbsp;</span>
308
+ <span class="cline-any cline-yes">3x</span>
295
309
  <span class="cline-any cline-neutral">&nbsp;</span>
296
- <span class="cline-any cline-yes">1x</span>
297
310
  <span class="cline-any cline-neutral">&nbsp;</span>
298
311
  <span class="cline-any cline-neutral">&nbsp;</span>
299
312
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -337,9 +350,9 @@
337
350
  <span class="cline-any cline-neutral">&nbsp;</span>
338
351
  <span class="cline-any cline-neutral">&nbsp;</span>
339
352
  <span class="cline-any cline-neutral">&nbsp;</span>
340
- <span class="cline-any cline-yes">1x</span>
341
- <span class="cline-any cline-yes">4x</span>
342
353
  <span class="cline-any cline-neutral">&nbsp;</span>
354
+ <span class="cline-any cline-yes">3x</span>
355
+ <span class="cline-any cline-yes">12x</span>
343
356
  <span class="cline-any cline-neutral">&nbsp;</span>
344
357
  <span class="cline-any cline-neutral">&nbsp;</span>
345
358
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -373,40 +386,49 @@
373
386
  <span class="cline-any cline-neutral">&nbsp;</span>
374
387
  <span class="cline-any cline-neutral">&nbsp;</span>
375
388
  <span class="cline-any cline-neutral">&nbsp;</span>
376
- <span class="cline-any cline-yes">4x</span>
377
- <span class="cline-any cline-yes">4x</span>
378
389
  <span class="cline-any cline-neutral">&nbsp;</span>
379
- <span class="cline-any cline-yes">4x</span>
380
390
  <span class="cline-any cline-neutral">&nbsp;</span>
381
- <span class="cline-any cline-yes">4x</span>
382
- <span class="cline-any cline-yes">104x</span>
383
391
  <span class="cline-any cline-neutral">&nbsp;</span>
392
+ <span class="cline-any cline-yes">12x</span>
393
+ <span class="cline-any cline-yes">12x</span>
384
394
  <span class="cline-any cline-neutral">&nbsp;</span>
385
- <span class="cline-any cline-yes">104x</span>
395
+ <span class="cline-any cline-yes">12x</span>
396
+ <span class="cline-any cline-neutral">&nbsp;</span>
397
+ <span class="cline-any cline-yes">12x</span>
398
+ <span class="cline-any cline-yes">336x</span>
399
+ <span class="cline-any cline-neutral">&nbsp;</span>
400
+ <span class="cline-any cline-neutral">&nbsp;</span>
401
+ <span class="cline-any cline-yes">336x</span>
386
402
  <span class="cline-any cline-no">&nbsp;</span>
387
403
  <span class="cline-any cline-neutral">&nbsp;</span>
388
404
  <span class="cline-any cline-neutral">&nbsp;</span>
389
405
  <span class="cline-any cline-neutral">&nbsp;</span>
390
- <span class="cline-any cline-yes">4x</span>
406
+ <span class="cline-any cline-yes">12x</span>
391
407
  <span class="cline-any cline-neutral">&nbsp;</span>
392
- <span class="cline-any cline-yes">4x</span>
393
- <span class="cline-any cline-yes">4x</span>
394
- <span class="cline-any cline-yes">4x</span>
408
+ <span class="cline-any cline-yes">12x</span>
409
+ <span class="cline-any cline-yes">12x</span>
410
+ <span class="cline-any cline-yes">12x</span>
395
411
  <span class="cline-any cline-no">&nbsp;</span>
396
412
  <span class="cline-any cline-neutral">&nbsp;</span>
397
413
  <span class="cline-any cline-neutral">&nbsp;</span>
398
- <span class="cline-any cline-yes">4x</span>
414
+ <span class="cline-any cline-yes">12x</span>
415
+ <span class="cline-any cline-neutral">&nbsp;</span>
399
416
  <span class="cline-any cline-neutral">&nbsp;</span>
417
+ <span class="cline-any cline-yes">12x</span>
418
+ <span class="cline-any cline-yes">1x</span>
400
419
  <span class="cline-any cline-neutral">&nbsp;</span>
401
- <span class="cline-any cline-yes">4x</span>
402
420
  <span class="cline-any cline-neutral">&nbsp;</span>
403
421
  <span class="cline-any cline-neutral">&nbsp;</span>
404
422
  <span class="cline-any cline-neutral">&nbsp;</span>
423
+ <span class="cline-any cline-yes">11x</span>
405
424
  <span class="cline-any cline-neutral">&nbsp;</span>
406
425
  <span class="cline-any cline-neutral">&nbsp;</span>
407
426
  <span class="cline-any cline-neutral">&nbsp;</span>
408
427
  <span class="cline-any cline-neutral">&nbsp;</span>
409
- <span class="cline-any cline-yes">1x</span>
428
+ <span class="cline-any cline-neutral">&nbsp;</span>
429
+ <span class="cline-any cline-neutral">&nbsp;</span>
430
+ <span class="cline-any cline-neutral">&nbsp;</span>
431
+ <span class="cline-any cline-yes">3x</span>
410
432
  <span class="cline-any cline-neutral">&nbsp;</span>
411
433
  <span class="cline-any cline-neutral">&nbsp;</span>
412
434
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -433,7 +455,7 @@
433
455
  <span class="cline-any cline-neutral">&nbsp;</span>
434
456
  <span class="cline-any cline-neutral">&nbsp;</span>
435
457
  <span class="cline-any cline-neutral">&nbsp;</span>
436
- <span class="cline-any cline-yes">1x</span>
458
+ <span class="cline-any cline-yes">3x</span>
437
459
  <span class="cline-any cline-neutral">&nbsp;</span>
438
460
  <span class="cline-any cline-neutral">&nbsp;</span>
439
461
  <span class="cline-any cline-no">&nbsp;</span>
@@ -477,6 +499,7 @@ export const DATAFILES_DIRECTORY_NAME = "datafiles";
477
499
  export const DATAFILE_NAME_PATTERN = "featurevisor-%s.json";
478
500
  export const REVISION_FILE_NAME = "REVISION";
479
501
  export const SITE_EXPORT_DIRECTORY_NAME = "out";
502
+ export const ENVIRONMENTS_DIRECTORY_NAME = "environments";
480
503
  &nbsp;
481
504
  export const CONFIG_MODULE_NAME = "featurevisor.config.js";
482
505
  export const ROOT_DIR_PLACEHOLDER = "&lt;rootDir&gt;";
@@ -511,8 +534,10 @@ export interface ProjectConfig {
511
534
  datafileNamePattern: string;
512
535
  revisionFileName: string;
513
536
  siteExportDirectoryPath: string;
537
+ environmentsDirectoryPath: string;
514
538
  &nbsp;
515
539
  environments: string[] | false;
540
+ splitByEnvironment: boolean;
516
541
  tags: string[];
517
542
  scopes?: Scope[];
518
543
  &nbsp;
@@ -550,6 +575,7 @@ export function getProjectConfig(rootDirectoryPath: string): ProjectConfig {
550
575
  adapter: FilesystemAdapter,
551
576
  &nbsp;
552
577
  featuresDirectoryPath: path.join(rootDirectoryPath, FEATURES_DIRECTORY_NAME),
578
+ environmentsDirectoryPath: path.join(rootDirectoryPath, ENVIRONMENTS_DIRECTORY_NAME),
553
579
  segmentsDirectoryPath: path.join(rootDirectoryPath, SEGMENTS_DIRECTORY_NAME),
554
580
  attributesDirectoryPath: path.join(rootDirectoryPath, ATTRIBUTES_DIRECTORY_NAME),
555
581
  groupsDirectoryPath: path.join(rootDirectoryPath, GROUPS_DIRECTORY_NAME),
@@ -563,6 +589,7 @@ export function getProjectConfig(rootDirectoryPath: string): ProjectConfig {
563
589
  &nbsp;
564
590
  enforceCatchAllRule: false,
565
591
  plugins: [],
592
+ splitByEnvironment: false,
566
593
  &nbsp;
567
594
  maxVariableStringLength: undefined,
568
595
  maxVariableArrayStringifiedLength: undefined,
@@ -594,6 +621,12 @@ export function getProjectConfig(rootDirectoryPath: string): ProjectConfig {
594
621
  &nbsp;
595
622
  finalConfig.parser = parsers[finalConfig.parser];
596
623
  }
624
+ &nbsp;
625
+ if (finalConfig.splitByEnvironment &amp;&amp; finalConfig.environments === false) {
626
+ throw new Error(
627
+ "Invalid configuration: splitByEnvironment=true requires environments to be an array",
628
+ );
629
+ }
597
630
  &nbsp;
598
631
  return finalConfig as ProjectConfig;
599
632
  }
@@ -661,7 +694,7 @@ export const configPlugin: Plugin = {
661
694
  <div class='footer quiet pad2 space-top1 center small'>
662
695
  Code coverage generated by
663
696
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
664
- at 2026-02-23T20:40:14.947Z
697
+ at 2026-02-25T22:03:15.189Z
665
698
  </div>
666
699
  <script src="../prettify.js"></script>
667
700
  <script>
@@ -23,9 +23,9 @@
23
23
  <div class='clearfix'>
24
24
 
25
25
  <div class='fl pad1y space-right2'>
26
- <span class="strong">100% </span>
26
+ <span class="strong">33.33% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>1/1</span>
28
+ <span class='fraction'>1/3</span>
29
29
  </div>
30
30
 
31
31
 
@@ -37,16 +37,16 @@
37
37
 
38
38
 
39
39
  <div class='fl pad1y space-right2'>
40
- <span class="strong">100% </span>
40
+ <span class="strong">0% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>0/0</span>
42
+ <span class='fraction'>0/2</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">100% </span>
47
+ <span class="strong">33.33% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>1/1</span>
49
+ <span class='fraction'>1/3</span>
50
50
  </div>
51
51
 
52
52
 
@@ -61,7 +61,7 @@
61
61
  </div>
62
62
  </template>
63
63
  </div>
64
- <div class='status-line high'></div>
64
+ <div class='status-line low'></div>
65
65
  <pre><table class="coverage">
66
66
  <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
67
  <a name='L2'></a><a href='#L2'>2</a>
@@ -113,7 +113,38 @@
113
113
  <a name='L48'></a><a href='#L48'>48</a>
114
114
  <a name='L49'></a><a href='#L49'>49</a>
115
115
  <a name='L50'></a><a href='#L50'>50</a>
116
- <a name='L51'></a><a href='#L51'>51</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
116
+ <a name='L51'></a><a href='#L51'>51</a>
117
+ <a name='L52'></a><a href='#L52'>52</a>
118
+ <a name='L53'></a><a href='#L53'>53</a>
119
+ <a name='L54'></a><a href='#L54'>54</a>
120
+ <a name='L55'></a><a href='#L55'>55</a>
121
+ <a name='L56'></a><a href='#L56'>56</a>
122
+ <a name='L57'></a><a href='#L57'>57</a>
123
+ <a name='L58'></a><a href='#L58'>58</a>
124
+ <a name='L59'></a><a href='#L59'>59</a>
125
+ <a name='L60'></a><a href='#L60'>60</a>
126
+ <a name='L61'></a><a href='#L61'>61</a>
127
+ <a name='L62'></a><a href='#L62'>62</a>
128
+ <a name='L63'></a><a href='#L63'>63</a>
129
+ <a name='L64'></a><a href='#L64'>64</a>
130
+ <a name='L65'></a><a href='#L65'>65</a>
131
+ <a name='L66'></a><a href='#L66'>66</a>
132
+ <a name='L67'></a><a href='#L67'>67</a>
133
+ <a name='L68'></a><a href='#L68'>68</a>
134
+ <a name='L69'></a><a href='#L69'>69</a>
135
+ <a name='L70'></a><a href='#L70'>70</a>
136
+ <a name='L71'></a><a href='#L71'>71</a>
137
+ <a name='L72'></a><a href='#L72'>72</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
138
+ <span class="cline-any cline-neutral">&nbsp;</span>
139
+ <span class="cline-any cline-neutral">&nbsp;</span>
140
+ <span class="cline-any cline-neutral">&nbsp;</span>
141
+ <span class="cline-any cline-neutral">&nbsp;</span>
142
+ <span class="cline-any cline-neutral">&nbsp;</span>
143
+ <span class="cline-any cline-neutral">&nbsp;</span>
144
+ <span class="cline-any cline-neutral">&nbsp;</span>
145
+ <span class="cline-any cline-neutral">&nbsp;</span>
146
+ <span class="cline-any cline-neutral">&nbsp;</span>
147
+ <span class="cline-any cline-neutral">&nbsp;</span>
117
148
  <span class="cline-any cline-neutral">&nbsp;</span>
118
149
  <span class="cline-any cline-neutral">&nbsp;</span>
119
150
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -130,9 +161,9 @@
130
161
  <span class="cline-any cline-neutral">&nbsp;</span>
131
162
  <span class="cline-any cline-neutral">&nbsp;</span>
132
163
  <span class="cline-any cline-neutral">&nbsp;</span>
164
+ <span class="cline-any cline-yes">3x</span>
133
165
  <span class="cline-any cline-neutral">&nbsp;</span>
134
166
  <span class="cline-any cline-neutral">&nbsp;</span>
135
- <span class="cline-any cline-yes">1x</span>
136
167
  <span class="cline-any cline-neutral">&nbsp;</span>
137
168
  <span class="cline-any cline-neutral">&nbsp;</span>
138
169
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -163,7 +194,19 @@
163
194
  <span class="cline-any cline-neutral">&nbsp;</span>
164
195
  <span class="cline-any cline-neutral">&nbsp;</span>
165
196
  <span class="cline-any cline-neutral">&nbsp;</span>
166
- <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import type {
197
+ <span class="cline-any cline-no">&nbsp;</span>
198
+ <span class="cline-any cline-neutral">&nbsp;</span>
199
+ <span class="cline-any cline-neutral">&nbsp;</span>
200
+ <span class="cline-any cline-neutral">&nbsp;</span>
201
+ <span class="cline-any cline-neutral">&nbsp;</span>
202
+ <span class="cline-any cline-neutral">&nbsp;</span>
203
+ <span class="cline-any cline-neutral">&nbsp;</span>
204
+ <span class="cline-any cline-neutral">&nbsp;</span>
205
+ <span class="cline-any cline-no">&nbsp;</span>
206
+ <span class="cline-any cline-neutral">&nbsp;</span>
207
+ <span class="cline-any cline-neutral">&nbsp;</span>
208
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">/* eslint-disable @typescript-eslint/no-unused-vars */
209
+ import type {
167
210
  DatafileContent,
168
211
  EnvironmentKey,
169
212
  ExistingState,
@@ -182,6 +225,13 @@ export interface DatafileOptions {
182
225
  datafilesDir?: string;
183
226
  }
184
227
  &nbsp;
228
+ export type FeatureEnvironmentProperty = "rules" | "force" | "expose";
229
+ &nbsp;
230
+ export interface FeatureSourcePaths {
231
+ baseFilePath: string;
232
+ environmentFilePaths: Record&lt;string, string&gt;;
233
+ }
234
+ &nbsp;
185
235
  export abstract class Adapter {
186
236
  // entities
187
237
  abstract listEntities(entityType: EntityType): Promise&lt;string[]&gt;;
@@ -212,6 +262,19 @@ export abstract class Adapter {
212
262
  entityType?: EntityType,
213
263
  entityKey?: string,
214
264
  ): Promise&lt;Commit&gt;;
265
+ &nbsp;
266
+ // feature source helpers (used by lint/history attribution)
267
+ <span class="fstat-no" title="function not covered" > async </span>getFeatureSourcePaths(_featureKey: string): Promise&lt;FeatureSourcePaths | undefined&gt; {
268
+ <span class="cstat-no" title="statement not covered" > return undefined;</span>
269
+ }
270
+ &nbsp;
271
+ <span class="fstat-no" title="function not covered" > async </span>getFeaturePropertySourcePath(
272
+ _featureKey: string,
273
+ _property: FeatureEnvironmentProperty,
274
+ _environment?: string,
275
+ ): Promise&lt;string | undefined&gt; {
276
+ <span class="cstat-no" title="statement not covered" > return undefined;</span>
277
+ }
215
278
  }
216
279
  &nbsp;</pre></td></tr></table></pre>
217
280
 
@@ -220,7 +283,7 @@ export abstract class Adapter {
220
283
  <div class='footer quiet pad2 space-top1 center small'>
221
284
  Code coverage generated by
222
285
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
223
- at 2026-02-23T20:40:14.947Z
286
+ at 2026-02-25T22:03:15.189Z
224
287
  </div>
225
288
  <script src="../prettify.js"></script>
226
289
  <script>