@featurevisor/core 2.27.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (389) hide show
  1. package/coverage/clover.xml +4991 -3130
  2. package/coverage/coverage-final.json +63 -39
  3. package/coverage/lcov-report/block-navigation.js +1 -1
  4. package/coverage/lcov-report/index.html +248 -98
  5. package/coverage/lcov-report/sorter.js +21 -7
  6. package/coverage/lcov-report/src/assess-distribution/index.html +116 -0
  7. package/coverage/lcov-report/src/assess-distribution/index.ts.html +754 -0
  8. package/coverage/lcov-report/{list → src/benchmark}/index.html +30 -30
  9. package/coverage/lcov-report/src/benchmark/index.ts.html +793 -0
  10. package/coverage/lcov-report/{builder → src/builder}/allocator.ts.html +38 -38
  11. package/coverage/lcov-report/{builder/buildScopedConditions.ts.html → src/builder/applyContextToConditions.ts.html} +80 -65
  12. package/coverage/lcov-report/{builder/buildScopedDatafile.ts.html → src/builder/applyContextToDatafile.ts.html} +92 -92
  13. package/coverage/lcov-report/{builder/buildScopedSegments.ts.html → src/builder/applyContextToSegments.ts.html} +103 -67
  14. package/coverage/lcov-report/{builder → src/builder}/buildDatafile.ts.html +336 -306
  15. package/coverage/lcov-report/src/builder/buildProject.ts.html +1117 -0
  16. package/coverage/lcov-report/src/builder/buildRuntimeDatafiles.ts.html +256 -0
  17. package/coverage/lcov-report/{builder → src/builder}/getFeatureRanges.ts.html +22 -22
  18. package/coverage/lcov-report/{builder → src/builder}/hashes.ts.html +50 -50
  19. package/coverage/lcov-report/{builder → src/builder}/index.html +114 -84
  20. package/coverage/lcov-report/{tester/cliFormat.ts.html → src/builder/index.ts.html} +17 -29
  21. package/coverage/lcov-report/{builder → src/builder}/mutateVariables.ts.html +55 -55
  22. package/coverage/lcov-report/{builder → src/builder}/mutator.ts.html +118 -118
  23. package/coverage/lcov-report/{builder → src/builder}/revision.ts.html +15 -15
  24. package/coverage/lcov-report/{builder → src/builder}/traffic.ts.html +86 -86
  25. package/coverage/lcov-report/src/cli/cli.ts.html +400 -0
  26. package/coverage/lcov-report/{tester → src/cli}/index.html +41 -41
  27. package/coverage/lcov-report/src/cli/plugins.ts.html +214 -0
  28. package/coverage/lcov-report/{config → src/config}/index.html +31 -31
  29. package/coverage/lcov-report/{config → src/config}/index.ts.html +12 -12
  30. package/coverage/lcov-report/{config → src/config}/projectConfig.ts.html +312 -96
  31. package/coverage/lcov-report/{datasource → src/datasource}/adapter.ts.html +28 -73
  32. package/coverage/lcov-report/{datasource → src/datasource}/datasource.ts.html +191 -104
  33. package/coverage/lcov-report/{datasource → src/datasource}/filesystemAdapter.ts.html +233 -635
  34. package/coverage/lcov-report/{datasource → src/datasource}/index.html +48 -48
  35. package/coverage/lcov-report/{datasource → src/datasource}/index.ts.html +14 -14
  36. package/coverage/lcov-report/src/evaluate/index.html +116 -0
  37. package/coverage/lcov-report/src/evaluate/index.ts.html +814 -0
  38. package/coverage/lcov-report/src/find-duplicate-segments/findDuplicateSegments.ts.html +313 -0
  39. package/coverage/lcov-report/src/find-duplicate-segments/index.html +131 -0
  40. package/coverage/lcov-report/src/find-duplicate-segments/index.ts.html +307 -0
  41. package/coverage/lcov-report/src/find-usage/index.html +116 -0
  42. package/coverage/lcov-report/src/find-usage/index.ts.html +1780 -0
  43. package/coverage/lcov-report/src/generate-code/index.html +131 -0
  44. package/coverage/lcov-report/src/generate-code/index.ts.html +403 -0
  45. package/coverage/lcov-report/{generate-code → src/generate-code}/typescript.ts.html +266 -1361
  46. package/coverage/lcov-report/{generate-code → src}/index.html +38 -23
  47. package/coverage/lcov-report/src/info/index.html +116 -0
  48. package/coverage/lcov-report/src/info/index.ts.html +472 -0
  49. package/coverage/lcov-report/src/init/index.html +116 -0
  50. package/coverage/lcov-report/src/init/index.ts.html +280 -0
  51. package/coverage/lcov-report/{linter → src/linter}/attributeSchema.ts.html +160 -157
  52. package/coverage/lcov-report/{linter → src/linter}/checkCircularDependency.ts.html +25 -25
  53. package/coverage/lcov-report/{linter → src/linter}/checkPercentageExceedingSlot.ts.html +42 -42
  54. package/coverage/lcov-report/{linter → src/linter}/conditionSchema.ts.html +207 -207
  55. package/coverage/lcov-report/{linter → src/linter}/featureSchema.ts.html +462 -471
  56. package/coverage/lcov-report/{linter → src/linter}/groupSchema.ts.html +25 -22
  57. package/coverage/lcov-report/{linter → src/linter}/index.html +129 -99
  58. package/coverage/lcov-report/src/linter/index.ts.html +88 -0
  59. package/coverage/lcov-report/{linter → src/linter}/lintProject.ts.html +740 -332
  60. package/coverage/lcov-report/{linter → src/linter}/mutationNotation.ts.html +130 -130
  61. package/coverage/lcov-report/{linter → src/linter}/printError.ts.html +27 -27
  62. package/coverage/lcov-report/{linter → src/linter}/schema.ts.html +153 -150
  63. package/coverage/lcov-report/{linter → src/linter}/segmentSchema.ts.html +18 -15
  64. package/coverage/lcov-report/src/linter/targetSchema.ts.html +223 -0
  65. package/coverage/lcov-report/{linter → src/linter}/testSchema.ts.html +54 -48
  66. package/coverage/lcov-report/{linter → src/linter}/zodHelpers.ts.html +17 -17
  67. package/coverage/lcov-report/src/list/index.html +131 -0
  68. package/coverage/lcov-report/src/list/index.ts.html +2065 -0
  69. package/coverage/lcov-report/{list → src/list}/matrix.ts.html +43 -58
  70. package/coverage/lcov-report/src/promoter/index.html +116 -0
  71. package/coverage/lcov-report/src/promoter/index.ts.html +3883 -0
  72. package/coverage/lcov-report/src/sets.ts.html +313 -0
  73. package/coverage/lcov-report/src/targeting.ts.html +403 -0
  74. package/coverage/lcov-report/src/tester/cliFormat.ts.html +133 -0
  75. package/coverage/lcov-report/{tester → src/tester}/helpers.ts.html +15 -15
  76. package/coverage/lcov-report/src/tester/index.html +206 -0
  77. package/coverage/lcov-report/src/tester/prettyDuration.ts.html +187 -0
  78. package/coverage/lcov-report/src/tester/printTestResult.ts.html +337 -0
  79. package/coverage/lcov-report/src/tester/testFeature.ts.html +1186 -0
  80. package/coverage/lcov-report/src/tester/testProject.ts.html +1018 -0
  81. package/coverage/lcov-report/src/tester/testSegment.ts.html +316 -0
  82. package/coverage/lcov-report/{utils → src/utils}/extractKeys.ts.html +68 -68
  83. package/coverage/lcov-report/{utils → src/utils}/git.ts.html +44 -68
  84. package/coverage/lcov-report/{utils → src/utils}/index.html +44 -44
  85. package/coverage/lcov-report/{utils → src/utils}/index.ts.html +14 -14
  86. package/coverage/lcov-report/{utils → src/utils}/pretty.ts.html +20 -20
  87. package/coverage/lcov.info +10121 -5723
  88. package/jest.config.js +13 -4
  89. package/lib/assess-distribution/index.d.ts +1 -1
  90. package/lib/assess-distribution/index.js +59 -40
  91. package/lib/assess-distribution/index.js.map +1 -1
  92. package/lib/benchmark/index.d.ts +8 -5
  93. package/lib/benchmark/index.js +82 -65
  94. package/lib/benchmark/index.js.map +1 -1
  95. package/lib/builder/applyContextToConditions.d.ts +5 -0
  96. package/lib/builder/{buildScopedConditions.js → applyContextToConditions.js} +19 -14
  97. package/lib/builder/applyContextToConditions.js.map +1 -0
  98. package/lib/builder/applyContextToDatafile.d.ts +2 -0
  99. package/lib/builder/{buildScopedDatafile.js → applyContextToDatafile.js} +26 -26
  100. package/lib/builder/applyContextToDatafile.js.map +1 -0
  101. package/lib/builder/applyContextToSegments.d.ts +5 -0
  102. package/lib/builder/{buildScopedSegments.js → applyContextToSegments.js} +19 -14
  103. package/lib/builder/applyContextToSegments.js.map +1 -0
  104. package/lib/builder/buildDatafile.d.ts +9 -6
  105. package/lib/builder/buildDatafile.js +86 -51
  106. package/lib/builder/buildDatafile.js.map +1 -1
  107. package/lib/builder/buildProject.d.ts +17 -3
  108. package/lib/builder/buildProject.js +151 -59
  109. package/lib/builder/buildProject.js.map +1 -1
  110. package/lib/builder/buildRuntimeDatafiles.d.ts +12 -0
  111. package/lib/builder/buildRuntimeDatafiles.js +35 -0
  112. package/lib/builder/buildRuntimeDatafiles.js.map +1 -0
  113. package/lib/builder/getFeatureRanges.js +34 -1
  114. package/lib/builder/getFeatureRanges.js.map +1 -1
  115. package/lib/builder/hashes.js +34 -1
  116. package/lib/builder/hashes.js.map +1 -1
  117. package/lib/builder/index.d.ts +0 -1
  118. package/lib/builder/index.js +0 -1
  119. package/lib/builder/index.js.map +1 -1
  120. package/lib/cli/cli.js +0 -4
  121. package/lib/cli/cli.js.map +1 -1
  122. package/lib/cli/plugins.js +7 -2
  123. package/lib/cli/plugins.js.map +1 -1
  124. package/lib/config/projectConfig.d.ts +17 -17
  125. package/lib/config/projectConfig.js +101 -15
  126. package/lib/config/projectConfig.js.map +1 -1
  127. package/lib/datasource/adapter.d.ts +7 -9
  128. package/lib/datasource/adapter.js +0 -7
  129. package/lib/datasource/adapter.js.map +1 -1
  130. package/lib/datasource/datasource.d.ts +15 -5
  131. package/lib/datasource/datasource.js +37 -9
  132. package/lib/datasource/datasource.js.map +1 -1
  133. package/lib/datasource/filesystemAdapter.d.ts +4 -10
  134. package/lib/datasource/filesystemAdapter.js +111 -149
  135. package/lib/datasource/filesystemAdapter.js.map +1 -1
  136. package/lib/evaluate/index.d.ts +1 -7
  137. package/lib/evaluate/index.js +81 -67
  138. package/lib/evaluate/index.js.map +1 -1
  139. package/lib/find-duplicate-segments/findDuplicateSegments.js +34 -1
  140. package/lib/find-duplicate-segments/findDuplicateSegments.js.map +1 -1
  141. package/lib/find-duplicate-segments/index.js +21 -13
  142. package/lib/find-duplicate-segments/index.js.map +1 -1
  143. package/lib/find-usage/index.js +57 -41
  144. package/lib/find-usage/index.js.map +1 -1
  145. package/lib/generate-code/index.d.ts +1 -1
  146. package/lib/generate-code/index.js +64 -21
  147. package/lib/generate-code/index.js.map +1 -1
  148. package/lib/generate-code/typescript.d.ts +1 -1
  149. package/lib/generate-code/typescript.js +59 -333
  150. package/lib/generate-code/typescript.js.map +1 -1
  151. package/lib/index.d.ts +2 -1
  152. package/lib/index.js +2 -1
  153. package/lib/index.js.map +1 -1
  154. package/lib/info/index.js +57 -16
  155. package/lib/info/index.js.map +1 -1
  156. package/lib/init/index.d.ts +2 -2
  157. package/lib/init/index.js +39 -3
  158. package/lib/init/index.js.map +1 -1
  159. package/lib/linter/attributeSchema.d.ts +1 -0
  160. package/lib/linter/attributeSchema.js +1 -0
  161. package/lib/linter/attributeSchema.js.map +1 -1
  162. package/lib/linter/conditionSchema.js +5 -5
  163. package/lib/linter/conditionSchema.js.map +1 -1
  164. package/lib/linter/featureSchema.d.ts +4 -1
  165. package/lib/linter/featureSchema.js +15 -13
  166. package/lib/linter/featureSchema.js.map +1 -1
  167. package/lib/linter/groupSchema.d.ts +1 -0
  168. package/lib/linter/groupSchema.js +1 -0
  169. package/lib/linter/groupSchema.js.map +1 -1
  170. package/lib/linter/lintProject.d.ts +1 -3
  171. package/lib/linter/lintProject.js +198 -68
  172. package/lib/linter/lintProject.js.map +1 -1
  173. package/lib/linter/schema.js +1 -0
  174. package/lib/linter/schema.js.map +1 -1
  175. package/lib/linter/segmentSchema.d.ts +1 -0
  176. package/lib/linter/segmentSchema.js +1 -0
  177. package/lib/linter/segmentSchema.js.map +1 -1
  178. package/lib/linter/targetSchema.d.ts +16 -0
  179. package/lib/linter/targetSchema.js +40 -0
  180. package/lib/linter/targetSchema.js.map +1 -0
  181. package/lib/linter/testSchema.d.ts +4 -3
  182. package/lib/linter/testSchema.js +9 -4
  183. package/lib/linter/testSchema.js.map +1 -1
  184. package/lib/list/index.d.ts +1 -0
  185. package/lib/list/index.js +131 -12
  186. package/lib/list/index.js.map +1 -1
  187. package/lib/list/matrix.js +3 -7
  188. package/lib/list/matrix.js.map +1 -1
  189. package/lib/promoter/index.d.ts +50 -0
  190. package/lib/promoter/index.js +880 -0
  191. package/lib/promoter/index.js.map +1 -0
  192. package/lib/sets.d.ts +11 -0
  193. package/lib/sets.js +81 -0
  194. package/lib/sets.js.map +1 -0
  195. package/lib/targeting.d.ts +14 -0
  196. package/lib/targeting.js +82 -0
  197. package/lib/targeting.js.map +1 -0
  198. package/lib/tester/cliFormat.d.ts +7 -1
  199. package/lib/tester/cliFormat.js +11 -2
  200. package/lib/tester/cliFormat.js.map +1 -1
  201. package/lib/tester/printTestResult.js +2 -2
  202. package/lib/tester/printTestResult.js.map +1 -1
  203. package/lib/tester/testFeature.d.ts +0 -1
  204. package/lib/tester/testFeature.js +8 -25
  205. package/lib/tester/testFeature.js.map +1 -1
  206. package/lib/tester/testProject.d.ts +2 -3
  207. package/lib/tester/testProject.js +93 -86
  208. package/lib/tester/testProject.js.map +1 -1
  209. package/lib/tester/testSegment.d.ts +1 -1
  210. package/lib/tester/testSegment.js +3 -21
  211. package/lib/tester/testSegment.js.map +1 -1
  212. package/lib/utils/git.js +45 -19
  213. package/lib/utils/git.js.map +1 -1
  214. package/package.json +12 -12
  215. package/src/assess-distribution/index.ts +72 -52
  216. package/src/benchmark/index.ts +124 -91
  217. package/src/builder/{buildScopedConditions.spec.ts → applyContextToConditions.spec.ts} +160 -142
  218. package/src/builder/{buildScopedConditions.ts → applyContextToConditions.ts} +19 -14
  219. package/src/builder/{buildScopedDatafile.spec.ts → applyContextToDatafile.spec.ts} +141 -81
  220. package/src/builder/{buildScopedDatafile.ts → applyContextToDatafile.ts} +33 -33
  221. package/src/builder/{buildScopedSegments.spec.ts → applyContextToSegments.spec.ts} +171 -151
  222. package/src/builder/{buildScopedSegments.ts → applyContextToSegments.ts} +26 -14
  223. package/src/builder/buildDatafile.spec.ts +192 -14
  224. package/src/builder/buildDatafile.ts +74 -64
  225. package/src/builder/buildProject.ts +171 -86
  226. package/src/builder/buildRuntimeDatafiles.ts +57 -0
  227. package/src/builder/index.ts +0 -1
  228. package/src/cli/cli.ts +0 -5
  229. package/src/cli/plugins.ts +7 -2
  230. package/src/config/projectConfig.spec.ts +171 -8
  231. package/src/config/projectConfig.ts +100 -28
  232. package/src/datasource/adapter.ts +8 -23
  233. package/src/datasource/datasource.ts +50 -21
  234. package/src/datasource/filesystemAdapter.spec.ts +133 -74
  235. package/src/datasource/filesystemAdapter.ts +93 -227
  236. package/src/evaluate/index.ts +109 -97
  237. package/src/find-duplicate-segments/index.ts +34 -16
  238. package/src/find-usage/index.ts +89 -44
  239. package/src/generate-code/index.ts +36 -24
  240. package/src/generate-code/typescript.spec.ts +106 -0
  241. package/src/generate-code/typescript.ts +33 -398
  242. package/src/index.ts +2 -1
  243. package/src/info/index.ts +72 -16
  244. package/src/init/index.ts +1 -1
  245. package/src/linter/attributeSchema.ts +1 -0
  246. package/src/linter/conditionSchema.spec.ts +23 -3
  247. package/src/linter/conditionSchema.ts +5 -5
  248. package/src/linter/featureSchema.spec.ts +107 -18
  249. package/src/linter/featureSchema.ts +25 -28
  250. package/src/linter/groupSchema.ts +1 -0
  251. package/src/linter/lintProject.spec.ts +131 -92
  252. package/src/linter/lintProject.ts +233 -97
  253. package/src/linter/schema.ts +1 -0
  254. package/src/linter/segmentSchema.spec.ts +5 -3
  255. package/src/linter/segmentSchema.ts +1 -0
  256. package/src/linter/targetSchema.spec.ts +159 -0
  257. package/src/linter/targetSchema.ts +46 -0
  258. package/src/linter/testSchema.spec.ts +70 -5
  259. package/src/linter/testSchema.ts +12 -10
  260. package/src/list/index.spec.ts +135 -0
  261. package/src/list/index.ts +188 -12
  262. package/src/list/matrix.ts +3 -8
  263. package/src/promoter/index.spec.ts +617 -0
  264. package/src/promoter/index.ts +1266 -0
  265. package/src/sets.ts +76 -0
  266. package/src/targeting.spec.ts +74 -0
  267. package/src/targeting.ts +106 -0
  268. package/src/tester/cliFormat.ts +11 -1
  269. package/src/tester/printTestResult.ts +12 -3
  270. package/src/tester/testFeature.ts +10 -36
  271. package/src/tester/testProject.spec.ts +50 -0
  272. package/src/tester/testProject.ts +89 -140
  273. package/src/tester/testSegment.ts +5 -22
  274. package/src/utils/git.ts +13 -21
  275. package/tsconfig.cjs.json +5 -1
  276. package/tsconfig.typecheck.json +9 -0
  277. package/coverage/lcov-report/builder/convertToV1.ts.html +0 -583
  278. package/lib/builder/allocator.spec.d.ts +0 -1
  279. package/lib/builder/allocator.spec.js +0 -85
  280. package/lib/builder/allocator.spec.js.map +0 -1
  281. package/lib/builder/buildDatafile.spec.d.ts +0 -1
  282. package/lib/builder/buildDatafile.spec.js +0 -233
  283. package/lib/builder/buildDatafile.spec.js.map +0 -1
  284. package/lib/builder/buildScopedConditions.d.ts +0 -5
  285. package/lib/builder/buildScopedConditions.js.map +0 -1
  286. package/lib/builder/buildScopedConditions.spec.d.ts +0 -1
  287. package/lib/builder/buildScopedConditions.spec.js +0 -2167
  288. package/lib/builder/buildScopedConditions.spec.js.map +0 -1
  289. package/lib/builder/buildScopedDatafile.d.ts +0 -2
  290. package/lib/builder/buildScopedDatafile.js.map +0 -1
  291. package/lib/builder/buildScopedDatafile.spec.d.ts +0 -1
  292. package/lib/builder/buildScopedDatafile.spec.js +0 -2277
  293. package/lib/builder/buildScopedDatafile.spec.js.map +0 -1
  294. package/lib/builder/buildScopedSegments.d.ts +0 -5
  295. package/lib/builder/buildScopedSegments.js.map +0 -1
  296. package/lib/builder/buildScopedSegments.spec.d.ts +0 -1
  297. package/lib/builder/buildScopedSegments.spec.js +0 -1062
  298. package/lib/builder/buildScopedSegments.spec.js.map +0 -1
  299. package/lib/builder/convertToV1.d.ts +0 -10
  300. package/lib/builder/convertToV1.js +0 -119
  301. package/lib/builder/convertToV1.js.map +0 -1
  302. package/lib/builder/mutateVariables.spec.d.ts +0 -1
  303. package/lib/builder/mutateVariables.spec.js +0 -1074
  304. package/lib/builder/mutateVariables.spec.js.map +0 -1
  305. package/lib/builder/mutator.spec.d.ts +0 -1
  306. package/lib/builder/mutator.spec.js +0 -368
  307. package/lib/builder/mutator.spec.js.map +0 -1
  308. package/lib/builder/revision.spec.d.ts +0 -1
  309. package/lib/builder/revision.spec.js +0 -21
  310. package/lib/builder/revision.spec.js.map +0 -1
  311. package/lib/builder/traffic.spec.d.ts +0 -1
  312. package/lib/builder/traffic.spec.js +0 -872
  313. package/lib/builder/traffic.spec.js.map +0 -1
  314. package/lib/config/projectConfig.spec.d.ts +0 -1
  315. package/lib/config/projectConfig.spec.js +0 -24
  316. package/lib/config/projectConfig.spec.js.map +0 -1
  317. package/lib/datasource/filesystemAdapter.spec.d.ts +0 -1
  318. package/lib/datasource/filesystemAdapter.spec.js +0 -88
  319. package/lib/datasource/filesystemAdapter.spec.js.map +0 -1
  320. package/lib/generate-code/typescript.spec.d.ts +0 -1
  321. package/lib/generate-code/typescript.spec.js +0 -78
  322. package/lib/generate-code/typescript.spec.js.map +0 -1
  323. package/lib/linter/attributeSchema.spec.d.ts +0 -1
  324. package/lib/linter/attributeSchema.spec.js +0 -266
  325. package/lib/linter/attributeSchema.spec.js.map +0 -1
  326. package/lib/linter/conditionSchema.spec.d.ts +0 -1
  327. package/lib/linter/conditionSchema.spec.js +0 -380
  328. package/lib/linter/conditionSchema.spec.js.map +0 -1
  329. package/lib/linter/featureSchema.spec.d.ts +0 -1
  330. package/lib/linter/featureSchema.spec.js +0 -1552
  331. package/lib/linter/featureSchema.spec.js.map +0 -1
  332. package/lib/linter/lintProject.spec.d.ts +0 -1
  333. package/lib/linter/lintProject.spec.js +0 -211
  334. package/lib/linter/lintProject.spec.js.map +0 -1
  335. package/lib/linter/mutationNotation.spec.d.ts +0 -1
  336. package/lib/linter/mutationNotation.spec.js +0 -598
  337. package/lib/linter/mutationNotation.spec.js.map +0 -1
  338. package/lib/linter/schema.spec.d.ts +0 -1
  339. package/lib/linter/schema.spec.js +0 -535
  340. package/lib/linter/schema.spec.js.map +0 -1
  341. package/lib/linter/segmentSchema.spec.d.ts +0 -1
  342. package/lib/linter/segmentSchema.spec.js +0 -303
  343. package/lib/linter/segmentSchema.spec.js.map +0 -1
  344. package/lib/linter/testSchema.spec.d.ts +0 -1
  345. package/lib/linter/testSchema.spec.js +0 -162
  346. package/lib/linter/testSchema.spec.js.map +0 -1
  347. package/lib/list/matrix.spec.d.ts +0 -1
  348. package/lib/list/matrix.spec.js +0 -42
  349. package/lib/list/matrix.spec.js.map +0 -1
  350. package/lib/site/exportSite.d.ts +0 -2
  351. package/lib/site/exportSite.js +0 -30
  352. package/lib/site/exportSite.js.map +0 -1
  353. package/lib/site/generateHistory.d.ts +0 -3
  354. package/lib/site/generateHistory.js +0 -31
  355. package/lib/site/generateHistory.js.map +0 -1
  356. package/lib/site/generateSiteSearchIndex.d.ts +0 -4
  357. package/lib/site/generateSiteSearchIndex.js +0 -195
  358. package/lib/site/generateSiteSearchIndex.js.map +0 -1
  359. package/lib/site/getLastModifiedFromHistory.d.ts +0 -2
  360. package/lib/site/getLastModifiedFromHistory.js +0 -18
  361. package/lib/site/getLastModifiedFromHistory.js.map +0 -1
  362. package/lib/site/getOwnerAndRepoFromUrl.d.ts +0 -4
  363. package/lib/site/getOwnerAndRepoFromUrl.js +0 -20
  364. package/lib/site/getOwnerAndRepoFromUrl.js.map +0 -1
  365. package/lib/site/getRelativePaths.d.ts +0 -6
  366. package/lib/site/getRelativePaths.js +0 -15
  367. package/lib/site/getRelativePaths.js.map +0 -1
  368. package/lib/site/getRepoDetails.d.ts +0 -8
  369. package/lib/site/getRepoDetails.js +0 -48
  370. package/lib/site/getRepoDetails.js.map +0 -1
  371. package/lib/site/index.d.ts +0 -2
  372. package/lib/site/index.js +0 -43
  373. package/lib/site/index.js.map +0 -1
  374. package/lib/site/serveSite.d.ts +0 -2
  375. package/lib/site/serveSite.js +0 -54
  376. package/lib/site/serveSite.js.map +0 -1
  377. package/lib/tester/helpers.spec.d.ts +0 -1
  378. package/lib/tester/helpers.spec.js +0 -115
  379. package/lib/tester/helpers.spec.js.map +0 -1
  380. package/src/builder/convertToV1.ts +0 -166
  381. package/src/site/exportSite.ts +0 -42
  382. package/src/site/generateHistory.ts +0 -40
  383. package/src/site/generateSiteSearchIndex.ts +0 -258
  384. package/src/site/getLastModifiedFromHistory.ts +0 -21
  385. package/src/site/getOwnerAndRepoFromUrl.ts +0 -17
  386. package/src/site/getRelativePaths.ts +0 -24
  387. package/src/site/getRepoDetails.ts +0 -62
  388. package/src/site/index.ts +0 -46
  389. package/src/site/serveSite.ts +0 -58
@@ -0,0 +1,617 @@
1
+ import * as fs from "fs";
2
+ import * as os from "os";
3
+ import * as path from "path";
4
+
5
+ import { getProjectConfig } from "../config";
6
+ import { Datasource } from "../datasource";
7
+ import { getCustomDatafile } from "../builder/buildDatafile";
8
+ import { promotePlugin, promoteProjectSets } from "./index";
9
+
10
+ async function writeFile(root: string, relativePath: string, content: string) {
11
+ const filePath = path.join(root, relativePath);
12
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
13
+ await fs.promises.writeFile(filePath, content);
14
+ }
15
+
16
+ async function createProject(options?: { configContent?: string; sets?: string[] }) {
17
+ const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "featurevisor-promote-"));
18
+ const configContent =
19
+ options?.configContent ??
20
+ ["module.exports = {", " sets: true,", " tags: ['all', 'web'],", "};", ""].join("\n");
21
+ const sets = options?.sets ?? ["dev", "staging"];
22
+
23
+ await writeFile(root, "featurevisor.config.js", configContent);
24
+
25
+ for (const set of sets) {
26
+ await writeFile(
27
+ root,
28
+ `sets/${set}/attributes/userId.yml`,
29
+ "description: User ID\ntype: string\n",
30
+ );
31
+ await writeFile(root, `sets/${set}/attributes/team.yml`, "description: Team\ntype: string\n");
32
+ await writeFile(
33
+ root,
34
+ `sets/${set}/schemas/payload.yml`,
35
+ "description: Payload\ntype: object\n",
36
+ );
37
+ await writeFile(
38
+ root,
39
+ `sets/${set}/segments/internal.yml`,
40
+ [
41
+ "description: Internal users",
42
+ "conditions:",
43
+ " - attribute: team",
44
+ " operator: equals",
45
+ " value: engineering",
46
+ "",
47
+ ].join("\n"),
48
+ );
49
+ await writeFile(
50
+ root,
51
+ `sets/${set}/features/checkoutFlow.yml`,
52
+ [
53
+ `description: Checkout flow in ${set}`,
54
+ "tags:",
55
+ " - all",
56
+ "bucketBy: userId",
57
+ "variablesSchema:",
58
+ " payload:",
59
+ " schema: payload",
60
+ " defaultValue: {}",
61
+ "rules:",
62
+ " - key: everyone",
63
+ set === "dev" ? " segments: internal" : ' segments: "*"',
64
+ set === "dev" ? " percentage: 100" : " percentage: 0",
65
+ "",
66
+ ].join("\n"),
67
+ );
68
+ await writeFile(
69
+ root,
70
+ `sets/${set}/tests/features/checkoutFlow.spec.yml`,
71
+ [
72
+ "feature: checkoutFlow",
73
+ "assertions:",
74
+ " - description: Checkout assertion",
75
+ " at: 10",
76
+ " context:",
77
+ ' userId: "user-1"',
78
+ " team: engineering",
79
+ set === "dev" ? " expectedToBeEnabled: true" : " expectedToBeEnabled: false",
80
+ "",
81
+ ].join("\n"),
82
+ );
83
+ await writeFile(
84
+ root,
85
+ `sets/${set}/tests/segments/internal.spec.yml`,
86
+ [
87
+ "segment: internal",
88
+ "assertions:",
89
+ " - context:",
90
+ " team: engineering",
91
+ " expectedToMatch: true",
92
+ "",
93
+ ].join("\n"),
94
+ );
95
+ }
96
+
97
+ return root;
98
+ }
99
+
100
+ describe("promoteProjectSets", function () {
101
+ it("previews by default and applies only with apply", async function () {
102
+ const root = await createProject();
103
+ const projectConfig = getProjectConfig(root);
104
+ const datasource = new Datasource(projectConfig, root);
105
+
106
+ const preview = await promoteProjectSets(projectConfig, datasource, {
107
+ from: "dev",
108
+ to: "staging",
109
+ includeFeatures: "checkout*",
110
+ });
111
+
112
+ expect(preview.apply).toEqual(false);
113
+ expect(preview.files.updated).toEqual(
114
+ expect.arrayContaining([expect.stringContaining("sets/staging/features/checkoutFlow.yml")]),
115
+ );
116
+ expect((await datasource.forSet("staging").readFeature("checkoutFlow")).description).toEqual(
117
+ "Checkout flow in staging",
118
+ );
119
+
120
+ const result = await promoteProjectSets(projectConfig, datasource, {
121
+ from: "dev",
122
+ to: "staging",
123
+ includeFeatures: "checkout*",
124
+ apply: true,
125
+ });
126
+ const feature = await datasource.forSet("staging").readFeature("checkoutFlow");
127
+
128
+ expect(result.apply).toEqual(true);
129
+ expect(result.dependencies.feature).toEqual(1);
130
+ expect(result.dependencies.segment).toEqual(1);
131
+ expect(result.dependencies.attribute).toEqual(2);
132
+ expect(result.dependencies.schema).toEqual(1);
133
+ expect(result.dependencies.test).toEqual(2);
134
+ expect(feature.description).toEqual("Checkout flow in dev");
135
+ });
136
+
137
+ it("uses all features as the starting set when only excludeFeatures is provided", async function () {
138
+ const root = await createProject();
139
+ await writeFile(
140
+ root,
141
+ "sets/dev/features/experimentalBanner.yml",
142
+ [
143
+ "description: Experimental banner",
144
+ "tags:",
145
+ " - all",
146
+ "bucketBy: userId",
147
+ "rules:",
148
+ " - key: everyone",
149
+ ' segments: "*"',
150
+ " percentage: 100",
151
+ "",
152
+ ].join("\n"),
153
+ );
154
+ const projectConfig = getProjectConfig(root);
155
+ const datasource = new Datasource(projectConfig, root);
156
+
157
+ const result = await promoteProjectSets(projectConfig, datasource, {
158
+ from: "dev",
159
+ to: "staging",
160
+ excludeFeatures: "experimental*",
161
+ });
162
+
163
+ expect(result.dependencies.feature).toEqual(1);
164
+ expect(result.files.updated).toEqual(
165
+ expect.arrayContaining([expect.stringContaining("features/checkoutFlow.yml")]),
166
+ );
167
+ expect(result.files.created).not.toEqual(
168
+ expect.arrayContaining([expect.stringContaining("features/experimentalBanner.yml")]),
169
+ );
170
+ });
171
+
172
+ it("selects a target and all definitions needed by its matching features", async function () {
173
+ const root = await createProject();
174
+ await writeFile(
175
+ root,
176
+ "sets/dev/targets/web.yml",
177
+ [
178
+ "description: Web datafile",
179
+ "tags:",
180
+ " and:",
181
+ " - web",
182
+ " - all",
183
+ "includeFeatures:",
184
+ " - web*",
185
+ "excludeFeatures:",
186
+ " - webInternal*",
187
+ "",
188
+ ].join("\n"),
189
+ );
190
+ for (const [key, segment] of [
191
+ ["webCheckout", "internal"],
192
+ ["webInternalTools", '"*"'],
193
+ ]) {
194
+ await writeFile(
195
+ root,
196
+ `sets/dev/features/${key}.yml`,
197
+ [
198
+ `description: ${key}`,
199
+ "tags:",
200
+ " - web",
201
+ " - all",
202
+ "bucketBy: userId",
203
+ "rules:",
204
+ " - key: everyone",
205
+ ` segments: ${segment}`,
206
+ " percentage: 100",
207
+ "",
208
+ ].join("\n"),
209
+ );
210
+ }
211
+ const projectConfig = getProjectConfig(root);
212
+ const datasource = new Datasource(projectConfig, root);
213
+
214
+ const result = await promoteProjectSets(projectConfig, datasource, {
215
+ from: "dev",
216
+ to: "staging",
217
+ target: "web",
218
+ apply: true,
219
+ });
220
+
221
+ expect(result.filters.targets).toEqual(["web"]);
222
+ expect(result.dependencies.target).toEqual(1);
223
+ expect(result.dependencies.feature).toEqual(1);
224
+ expect(result.dependencies.segment).toEqual(1);
225
+ expect(result.dependencies.attribute).toEqual(2);
226
+ expect(result.files.created).toEqual(
227
+ expect.arrayContaining([
228
+ expect.stringContaining("targets/web.yml"),
229
+ expect.stringContaining("features/webCheckout.yml"),
230
+ ]),
231
+ );
232
+ expect(result.files.created).not.toEqual(
233
+ expect.arrayContaining([expect.stringContaining("features/webInternalTools.yml")]),
234
+ );
235
+
236
+ const destinationDatasource = datasource.forSet("staging");
237
+ const target = await destinationDatasource.readTarget("web");
238
+ const datafile = await getCustomDatafile({
239
+ environment: false,
240
+ projectConfig: destinationDatasource.getConfig(),
241
+ datasource: destinationDatasource,
242
+ revision: "test",
243
+ tag: target.tag,
244
+ tags: target.tags,
245
+ includeFeatures: target.includeFeatures,
246
+ excludeFeatures: target.excludeFeatures,
247
+ });
248
+ expect(Object.keys(datafile.features)).toEqual(["webCheckout"]);
249
+ expect(Object.keys(datafile.segments)).toEqual(["internal"]);
250
+ });
251
+
252
+ it("selects features by tag and includes exclusion group members", async function () {
253
+ const root = await createProject();
254
+ await writeFile(
255
+ root,
256
+ "sets/dev/features/webCheckout.yml",
257
+ [
258
+ "description: Web checkout",
259
+ "tags:",
260
+ " - web",
261
+ "bucketBy: userId",
262
+ "rules:",
263
+ " - key: everyone",
264
+ ' segments: "*"',
265
+ " percentage: 50",
266
+ "",
267
+ ].join("\n"),
268
+ );
269
+ await writeFile(
270
+ root,
271
+ "sets/dev/features/checkoutFlow.yml",
272
+ [
273
+ "description: Checkout flow in dev",
274
+ "tags:",
275
+ " - all",
276
+ "bucketBy: userId",
277
+ "rules:",
278
+ " - key: everyone",
279
+ " segments: internal",
280
+ " percentage: 50",
281
+ "",
282
+ ].join("\n"),
283
+ );
284
+ await writeFile(
285
+ root,
286
+ "sets/dev/groups/webExperiments.yml",
287
+ [
288
+ "description: Web experiments",
289
+ "slots:",
290
+ " - feature: webCheckout",
291
+ " percentage: 50",
292
+ " - feature: checkoutFlow",
293
+ " percentage: 50",
294
+ "",
295
+ ].join("\n"),
296
+ );
297
+ const projectConfig = getProjectConfig(root);
298
+ const datasource = new Datasource(projectConfig, root);
299
+
300
+ const result = await promoteProjectSets(projectConfig, datasource, {
301
+ from: "dev",
302
+ to: "staging",
303
+ tag: "web",
304
+ });
305
+
306
+ expect(result.filters.tags).toEqual(["web"]);
307
+ expect(result.dependencies.feature).toEqual(2);
308
+ expect(result.dependencies.group).toEqual(1);
309
+ expect(result.dependencies.segment).toEqual(1);
310
+ });
311
+
312
+ it("rejects unknown targets and empty filtered results unless allowEmpty is used", async function () {
313
+ const root = await createProject();
314
+ const projectConfig = getProjectConfig(root);
315
+ const datasource = new Datasource(projectConfig, root);
316
+
317
+ await expect(
318
+ promoteProjectSets(projectConfig, datasource, {
319
+ from: "dev",
320
+ to: "staging",
321
+ target: "missing",
322
+ }),
323
+ ).rejects.toThrow('Unknown source target "missing"');
324
+
325
+ await expect(
326
+ promoteProjectSets(projectConfig, datasource, {
327
+ from: "dev",
328
+ to: "staging",
329
+ tag: "missing",
330
+ }),
331
+ ).rejects.toThrow('Unknown source tag "missing"');
332
+
333
+ await expect(
334
+ promoteProjectSets(projectConfig, datasource, {
335
+ from: "dev",
336
+ to: "staging",
337
+ tag: "web",
338
+ }),
339
+ ).rejects.toThrow("No source features matched the promotion filters.");
340
+
341
+ const empty = await promoteProjectSets(projectConfig, datasource, {
342
+ from: "dev",
343
+ to: "staging",
344
+ tag: "web",
345
+ allowEmpty: true,
346
+ });
347
+ expect(empty.dependencies.feature).toEqual(0);
348
+ });
349
+
350
+ it("validates sets and promotion flow constraints", async function () {
351
+ const root = await createProject({
352
+ sets: ["dev", "staging", "production"],
353
+ configContent: [
354
+ "module.exports = {",
355
+ " sets: true,",
356
+ " promotionFlows: [",
357
+ ' { from: "dev", to: "staging" },',
358
+ ' { from: "staging", to: "production" },',
359
+ " ],",
360
+ "};",
361
+ "",
362
+ ].join("\n"),
363
+ });
364
+ const projectConfig = getProjectConfig(root);
365
+ const datasource = new Datasource(projectConfig, root);
366
+
367
+ await expect(
368
+ promoteProjectSets(projectConfig, datasource, { from: "dev", to: "dev" }),
369
+ ).rejects.toThrow("--from and --to must be different sets.");
370
+ await expect(
371
+ promoteProjectSets(projectConfig, datasource, { from: "missing", to: "staging" }),
372
+ ).rejects.toThrow('Unknown source set "missing"');
373
+ await expect(
374
+ promoteProjectSets(projectConfig, datasource, { from: "dev", to: "production" }),
375
+ ).rejects.toThrow(
376
+ 'Promotion from "dev" to "production" is not allowed by this project\'s configured promotionFlows.',
377
+ );
378
+
379
+ const allowed = await promoteProjectSets(projectConfig, datasource, {
380
+ from: "dev",
381
+ to: "staging",
382
+ });
383
+
384
+ expect(allowed.from).toEqual("dev");
385
+ expect(allowed.to).toEqual("staging");
386
+ });
387
+
388
+ it("blocks all promotions when promotionFlows is empty", async function () {
389
+ const root = await createProject({
390
+ configContent: [
391
+ "module.exports = {",
392
+ " sets: true,",
393
+ " promotionFlows: [],",
394
+ "};",
395
+ "",
396
+ ].join("\n"),
397
+ });
398
+ const projectConfig = getProjectConfig(root);
399
+ const datasource = new Datasource(projectConfig, root);
400
+
401
+ await expect(
402
+ promoteProjectSets(projectConfig, datasource, { from: "dev", to: "staging" }),
403
+ ).rejects.toThrow(
404
+ 'Promotion from "dev" to "staging" is not allowed by this project\'s configured promotionFlows.',
405
+ );
406
+ });
407
+
408
+ it("skips updates when either existing entity is non-promotable but still creates missing entities", async function () {
409
+ const root = await createProject();
410
+ await writeFile(
411
+ root,
412
+ "sets/dev/features/checkoutFlow.yml",
413
+ [
414
+ "description: Protected checkout",
415
+ "promotable: false",
416
+ "tags:",
417
+ " - all",
418
+ "bucketBy: userId",
419
+ "rules:",
420
+ " - key: everyone",
421
+ ' segments: "*"',
422
+ " percentage: 100",
423
+ "",
424
+ ].join("\n"),
425
+ );
426
+ await writeFile(
427
+ root,
428
+ "sets/dev/features/mobileBanner.yml",
429
+ [
430
+ "description: Mobile banner",
431
+ "promotable: false",
432
+ "tags:",
433
+ " - all",
434
+ "bucketBy: userId",
435
+ "rules:",
436
+ " - key: everyone",
437
+ ' segments: "*"',
438
+ " percentage: 100",
439
+ "",
440
+ ].join("\n"),
441
+ );
442
+ const projectConfig = getProjectConfig(root);
443
+ const datasource = new Datasource(projectConfig, root);
444
+
445
+ const result = await promoteProjectSets(projectConfig, datasource, {
446
+ from: "dev",
447
+ to: "staging",
448
+ apply: true,
449
+ });
450
+
451
+ expect(result.files.unchanged).toEqual(
452
+ expect.arrayContaining([expect.stringContaining("sets/staging/features/checkoutFlow.yml")]),
453
+ );
454
+ expect(result.files.created).toEqual(
455
+ expect.arrayContaining([expect.stringContaining("sets/staging/features/mobileBanner.yml")]),
456
+ );
457
+ expect((await datasource.forSet("staging").readFeature("checkoutFlow")).description).toEqual(
458
+ "Checkout flow in staging",
459
+ );
460
+ expect((await datasource.forSet("staging").readFeature("mobileBanner")).promotable).toEqual(
461
+ false,
462
+ );
463
+ });
464
+
465
+ it("skips source rules and preserves destination rules marked non-promotable", async function () {
466
+ const root = await createProject();
467
+ await writeFile(
468
+ root,
469
+ "sets/dev/features/checkoutFlow.yml",
470
+ [
471
+ "description: Checkout flow in dev",
472
+ "tags:",
473
+ " - all",
474
+ "bucketBy: userId",
475
+ "rules:",
476
+ " - key: everyone",
477
+ ' segments: "*"',
478
+ " percentage: 100",
479
+ " - key: dev-only",
480
+ " promotable: false",
481
+ ' segments: "*"',
482
+ " percentage: 100",
483
+ " - key: protected",
484
+ ' segments: "*"',
485
+ " percentage: 100",
486
+ "",
487
+ ].join("\n"),
488
+ );
489
+ await writeFile(
490
+ root,
491
+ "sets/staging/features/checkoutFlow.yml",
492
+ [
493
+ "description: Checkout flow in staging",
494
+ "tags:",
495
+ " - all",
496
+ "bucketBy: userId",
497
+ "rules:",
498
+ " - key: everyone",
499
+ ' segments: "*"',
500
+ " percentage: 0",
501
+ " - key: protected",
502
+ " promotable: false",
503
+ ' segments: "*"',
504
+ " percentage: 25",
505
+ "",
506
+ ].join("\n"),
507
+ );
508
+ const projectConfig = getProjectConfig(root);
509
+ const datasource = new Datasource(projectConfig, root);
510
+
511
+ await promoteProjectSets(projectConfig, datasource, {
512
+ from: "dev",
513
+ to: "staging",
514
+ includeFeatures: "checkout*",
515
+ apply: true,
516
+ });
517
+
518
+ const feature = await datasource.forSet("staging").readFeature("checkoutFlow");
519
+ const rules = feature.rules as any[];
520
+
521
+ expect(rules.map((rule) => rule.key)).toEqual(["everyone", "protected"]);
522
+ expect(rules.find((rule) => rule.key === "everyone").percentage).toEqual(100);
523
+ expect(rules.find((rule) => rule.key === "protected").percentage).toEqual(25);
524
+ expect(rules.find((rule) => rule.key === "protected").promotable).toEqual(false);
525
+ });
526
+
527
+ it("supports conflict policies", async function () {
528
+ const root = await createProject();
529
+ const projectConfig = getProjectConfig(root);
530
+ const datasource = new Datasource(projectConfig, root);
531
+
532
+ const destination = await promoteProjectSets(projectConfig, datasource, {
533
+ from: "dev",
534
+ to: "staging",
535
+ includeFeatures: "checkout*",
536
+ conflicts: "destination",
537
+ apply: true,
538
+ });
539
+
540
+ expect(destination.conflicts.length).toBeGreaterThan(0);
541
+ expect((await datasource.forSet("staging").readFeature("checkoutFlow")).description).toEqual(
542
+ "Checkout flow in staging",
543
+ );
544
+
545
+ await expect(
546
+ promoteProjectSets(projectConfig, datasource, {
547
+ from: "dev",
548
+ to: "staging",
549
+ includeFeatures: "checkout*",
550
+ conflicts: "fail",
551
+ }),
552
+ ).rejects.toThrow("conflict(s) and --conflicts=fail was used");
553
+ });
554
+
555
+ it("writes audit files only in apply mode", async function () {
556
+ jest.useFakeTimers().setSystemTime(new Date("2026-04-19T10:20:30Z"));
557
+
558
+ try {
559
+ const root = await createProject();
560
+ const projectConfig = getProjectConfig(root);
561
+ const datasource = new Datasource(projectConfig, root);
562
+
563
+ const preview = await promoteProjectSets(projectConfig, datasource, {
564
+ from: "dev",
565
+ to: "staging",
566
+ audit: "markdown",
567
+ });
568
+ const applied = await promoteProjectSets(projectConfig, datasource, {
569
+ from: "dev",
570
+ to: "staging",
571
+ apply: true,
572
+ audit: "markdown",
573
+ });
574
+
575
+ expect(preview.auditFilePath).toBeUndefined();
576
+ expect(applied.auditFilePath).toContain(
577
+ ".featurevisor/promotions/20260419T102030-dev-to-staging.md",
578
+ );
579
+
580
+ const audit = await fs.promises.readFile(path.resolve(root, applied.auditFilePath!), "utf8");
581
+ expect(audit).toContain("# Featurevisor Promotion");
582
+ expect(audit).toContain("- Mode: apply");
583
+ expect(audit).toContain("features/checkoutFlow.yml");
584
+ } finally {
585
+ jest.useRealTimers();
586
+ }
587
+ });
588
+
589
+ it("prints CLI preview output without unchanged files by default", async function () {
590
+ const root = await createProject();
591
+ const projectConfig = getProjectConfig(root);
592
+ const datasource = new Datasource(projectConfig, root);
593
+ const consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => undefined);
594
+
595
+ try {
596
+ await promotePlugin.handler({
597
+ rootDirectoryPath: root,
598
+ projectConfig,
599
+ datasource,
600
+ parsed: {
601
+ _: ["promote"],
602
+ from: "dev",
603
+ to: "staging",
604
+ },
605
+ });
606
+
607
+ const output = consoleLogSpy.mock.calls.flat().join("\n");
608
+
609
+ expect(output).toContain("Mode: preview");
610
+ expect(output).toContain("Promotion preview complete");
611
+ expect(output).toContain("Unchanged: ");
612
+ expect(output).not.toContain("Unchanged\n");
613
+ } finally {
614
+ consoleLogSpy.mockRestore();
615
+ }
616
+ });
617
+ });