@featurevisor/core 2.26.1 → 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 (390) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/coverage/clover.xml +4991 -3130
  3. package/coverage/coverage-final.json +63 -39
  4. package/coverage/lcov-report/block-navigation.js +1 -1
  5. package/coverage/lcov-report/index.html +248 -98
  6. package/coverage/lcov-report/sorter.js +21 -7
  7. package/coverage/lcov-report/src/assess-distribution/index.html +116 -0
  8. package/coverage/lcov-report/src/assess-distribution/index.ts.html +754 -0
  9. package/coverage/lcov-report/{list → src/benchmark}/index.html +30 -30
  10. package/coverage/lcov-report/src/benchmark/index.ts.html +793 -0
  11. package/coverage/lcov-report/{builder → src/builder}/allocator.ts.html +38 -38
  12. package/coverage/lcov-report/{builder/buildScopedConditions.ts.html → src/builder/applyContextToConditions.ts.html} +80 -65
  13. package/coverage/lcov-report/{builder/buildScopedDatafile.ts.html → src/builder/applyContextToDatafile.ts.html} +92 -92
  14. package/coverage/lcov-report/{builder/buildScopedSegments.ts.html → src/builder/applyContextToSegments.ts.html} +103 -67
  15. package/coverage/lcov-report/{builder → src/builder}/buildDatafile.ts.html +336 -306
  16. package/coverage/lcov-report/src/builder/buildProject.ts.html +1117 -0
  17. package/coverage/lcov-report/src/builder/buildRuntimeDatafiles.ts.html +256 -0
  18. package/coverage/lcov-report/{builder → src/builder}/getFeatureRanges.ts.html +22 -22
  19. package/coverage/lcov-report/{builder → src/builder}/hashes.ts.html +50 -50
  20. package/coverage/lcov-report/{builder → src/builder}/index.html +114 -84
  21. package/coverage/lcov-report/{tester/cliFormat.ts.html → src/builder/index.ts.html} +17 -29
  22. package/coverage/lcov-report/{builder → src/builder}/mutateVariables.ts.html +55 -55
  23. package/coverage/lcov-report/{builder → src/builder}/mutator.ts.html +118 -118
  24. package/coverage/lcov-report/{builder → src/builder}/revision.ts.html +15 -15
  25. package/coverage/lcov-report/{builder → src/builder}/traffic.ts.html +86 -86
  26. package/coverage/lcov-report/src/cli/cli.ts.html +400 -0
  27. package/coverage/lcov-report/{tester → src/cli}/index.html +41 -41
  28. package/coverage/lcov-report/src/cli/plugins.ts.html +214 -0
  29. package/coverage/lcov-report/{config → src/config}/index.html +31 -31
  30. package/coverage/lcov-report/{config → src/config}/index.ts.html +12 -12
  31. package/coverage/lcov-report/{config → src/config}/projectConfig.ts.html +312 -96
  32. package/coverage/lcov-report/{datasource → src/datasource}/adapter.ts.html +28 -73
  33. package/coverage/lcov-report/{datasource → src/datasource}/datasource.ts.html +191 -104
  34. package/coverage/lcov-report/{datasource → src/datasource}/filesystemAdapter.ts.html +233 -635
  35. package/coverage/lcov-report/{datasource → src/datasource}/index.html +48 -48
  36. package/coverage/lcov-report/{datasource → src/datasource}/index.ts.html +14 -14
  37. package/coverage/lcov-report/src/evaluate/index.html +116 -0
  38. package/coverage/lcov-report/src/evaluate/index.ts.html +814 -0
  39. package/coverage/lcov-report/src/find-duplicate-segments/findDuplicateSegments.ts.html +313 -0
  40. package/coverage/lcov-report/src/find-duplicate-segments/index.html +131 -0
  41. package/coverage/lcov-report/src/find-duplicate-segments/index.ts.html +307 -0
  42. package/coverage/lcov-report/src/find-usage/index.html +116 -0
  43. package/coverage/lcov-report/src/find-usage/index.ts.html +1780 -0
  44. package/coverage/lcov-report/src/generate-code/index.html +131 -0
  45. package/coverage/lcov-report/src/generate-code/index.ts.html +403 -0
  46. package/coverage/lcov-report/{generate-code → src/generate-code}/typescript.ts.html +266 -1361
  47. package/coverage/lcov-report/{generate-code → src}/index.html +38 -23
  48. package/coverage/lcov-report/src/info/index.html +116 -0
  49. package/coverage/lcov-report/src/info/index.ts.html +472 -0
  50. package/coverage/lcov-report/src/init/index.html +116 -0
  51. package/coverage/lcov-report/src/init/index.ts.html +280 -0
  52. package/coverage/lcov-report/{linter → src/linter}/attributeSchema.ts.html +160 -157
  53. package/coverage/lcov-report/{linter → src/linter}/checkCircularDependency.ts.html +25 -25
  54. package/coverage/lcov-report/{linter → src/linter}/checkPercentageExceedingSlot.ts.html +42 -42
  55. package/coverage/lcov-report/{linter → src/linter}/conditionSchema.ts.html +207 -207
  56. package/coverage/lcov-report/{linter → src/linter}/featureSchema.ts.html +462 -471
  57. package/coverage/lcov-report/{linter → src/linter}/groupSchema.ts.html +25 -22
  58. package/coverage/lcov-report/{linter → src/linter}/index.html +129 -99
  59. package/coverage/lcov-report/src/linter/index.ts.html +88 -0
  60. package/coverage/lcov-report/{linter → src/linter}/lintProject.ts.html +740 -332
  61. package/coverage/lcov-report/{linter → src/linter}/mutationNotation.ts.html +130 -130
  62. package/coverage/lcov-report/{linter → src/linter}/printError.ts.html +27 -27
  63. package/coverage/lcov-report/{linter → src/linter}/schema.ts.html +153 -150
  64. package/coverage/lcov-report/{linter → src/linter}/segmentSchema.ts.html +18 -15
  65. package/coverage/lcov-report/src/linter/targetSchema.ts.html +223 -0
  66. package/coverage/lcov-report/{linter → src/linter}/testSchema.ts.html +54 -48
  67. package/coverage/lcov-report/{linter → src/linter}/zodHelpers.ts.html +17 -17
  68. package/coverage/lcov-report/src/list/index.html +131 -0
  69. package/coverage/lcov-report/src/list/index.ts.html +2065 -0
  70. package/coverage/lcov-report/{list → src/list}/matrix.ts.html +43 -58
  71. package/coverage/lcov-report/src/promoter/index.html +116 -0
  72. package/coverage/lcov-report/src/promoter/index.ts.html +3883 -0
  73. package/coverage/lcov-report/src/sets.ts.html +313 -0
  74. package/coverage/lcov-report/src/targeting.ts.html +403 -0
  75. package/coverage/lcov-report/src/tester/cliFormat.ts.html +133 -0
  76. package/coverage/lcov-report/{tester → src/tester}/helpers.ts.html +15 -15
  77. package/coverage/lcov-report/src/tester/index.html +206 -0
  78. package/coverage/lcov-report/src/tester/prettyDuration.ts.html +187 -0
  79. package/coverage/lcov-report/src/tester/printTestResult.ts.html +337 -0
  80. package/coverage/lcov-report/src/tester/testFeature.ts.html +1186 -0
  81. package/coverage/lcov-report/src/tester/testProject.ts.html +1018 -0
  82. package/coverage/lcov-report/src/tester/testSegment.ts.html +316 -0
  83. package/coverage/lcov-report/{utils → src/utils}/extractKeys.ts.html +68 -68
  84. package/coverage/lcov-report/{utils → src/utils}/git.ts.html +44 -68
  85. package/coverage/lcov-report/{utils → src/utils}/index.html +44 -44
  86. package/coverage/lcov-report/{utils → src/utils}/index.ts.html +14 -14
  87. package/coverage/lcov-report/{utils → src/utils}/pretty.ts.html +20 -20
  88. package/coverage/lcov.info +10121 -5723
  89. package/jest.config.js +13 -4
  90. package/lib/assess-distribution/index.d.ts +1 -1
  91. package/lib/assess-distribution/index.js +59 -40
  92. package/lib/assess-distribution/index.js.map +1 -1
  93. package/lib/benchmark/index.d.ts +8 -5
  94. package/lib/benchmark/index.js +82 -65
  95. package/lib/benchmark/index.js.map +1 -1
  96. package/lib/builder/applyContextToConditions.d.ts +5 -0
  97. package/lib/builder/{buildScopedConditions.js → applyContextToConditions.js} +19 -14
  98. package/lib/builder/applyContextToConditions.js.map +1 -0
  99. package/lib/builder/applyContextToDatafile.d.ts +2 -0
  100. package/lib/builder/{buildScopedDatafile.js → applyContextToDatafile.js} +26 -26
  101. package/lib/builder/applyContextToDatafile.js.map +1 -0
  102. package/lib/builder/applyContextToSegments.d.ts +5 -0
  103. package/lib/builder/{buildScopedSegments.js → applyContextToSegments.js} +19 -14
  104. package/lib/builder/applyContextToSegments.js.map +1 -0
  105. package/lib/builder/buildDatafile.d.ts +9 -6
  106. package/lib/builder/buildDatafile.js +86 -51
  107. package/lib/builder/buildDatafile.js.map +1 -1
  108. package/lib/builder/buildProject.d.ts +17 -3
  109. package/lib/builder/buildProject.js +151 -59
  110. package/lib/builder/buildProject.js.map +1 -1
  111. package/lib/builder/buildRuntimeDatafiles.d.ts +12 -0
  112. package/lib/builder/buildRuntimeDatafiles.js +35 -0
  113. package/lib/builder/buildRuntimeDatafiles.js.map +1 -0
  114. package/lib/builder/getFeatureRanges.js +34 -1
  115. package/lib/builder/getFeatureRanges.js.map +1 -1
  116. package/lib/builder/hashes.js +34 -1
  117. package/lib/builder/hashes.js.map +1 -1
  118. package/lib/builder/index.d.ts +0 -1
  119. package/lib/builder/index.js +0 -1
  120. package/lib/builder/index.js.map +1 -1
  121. package/lib/cli/cli.js +0 -4
  122. package/lib/cli/cli.js.map +1 -1
  123. package/lib/cli/plugins.js +7 -2
  124. package/lib/cli/plugins.js.map +1 -1
  125. package/lib/config/projectConfig.d.ts +17 -17
  126. package/lib/config/projectConfig.js +101 -15
  127. package/lib/config/projectConfig.js.map +1 -1
  128. package/lib/datasource/adapter.d.ts +7 -9
  129. package/lib/datasource/adapter.js +0 -7
  130. package/lib/datasource/adapter.js.map +1 -1
  131. package/lib/datasource/datasource.d.ts +15 -5
  132. package/lib/datasource/datasource.js +37 -9
  133. package/lib/datasource/datasource.js.map +1 -1
  134. package/lib/datasource/filesystemAdapter.d.ts +4 -10
  135. package/lib/datasource/filesystemAdapter.js +111 -149
  136. package/lib/datasource/filesystemAdapter.js.map +1 -1
  137. package/lib/evaluate/index.d.ts +1 -7
  138. package/lib/evaluate/index.js +81 -67
  139. package/lib/evaluate/index.js.map +1 -1
  140. package/lib/find-duplicate-segments/findDuplicateSegments.js +34 -1
  141. package/lib/find-duplicate-segments/findDuplicateSegments.js.map +1 -1
  142. package/lib/find-duplicate-segments/index.js +21 -13
  143. package/lib/find-duplicate-segments/index.js.map +1 -1
  144. package/lib/find-usage/index.js +57 -41
  145. package/lib/find-usage/index.js.map +1 -1
  146. package/lib/generate-code/index.d.ts +1 -1
  147. package/lib/generate-code/index.js +64 -21
  148. package/lib/generate-code/index.js.map +1 -1
  149. package/lib/generate-code/typescript.d.ts +1 -1
  150. package/lib/generate-code/typescript.js +59 -333
  151. package/lib/generate-code/typescript.js.map +1 -1
  152. package/lib/index.d.ts +2 -1
  153. package/lib/index.js +2 -1
  154. package/lib/index.js.map +1 -1
  155. package/lib/info/index.js +57 -16
  156. package/lib/info/index.js.map +1 -1
  157. package/lib/init/index.d.ts +2 -2
  158. package/lib/init/index.js +39 -3
  159. package/lib/init/index.js.map +1 -1
  160. package/lib/linter/attributeSchema.d.ts +1 -0
  161. package/lib/linter/attributeSchema.js +1 -0
  162. package/lib/linter/attributeSchema.js.map +1 -1
  163. package/lib/linter/conditionSchema.js +5 -5
  164. package/lib/linter/conditionSchema.js.map +1 -1
  165. package/lib/linter/featureSchema.d.ts +4 -1
  166. package/lib/linter/featureSchema.js +15 -13
  167. package/lib/linter/featureSchema.js.map +1 -1
  168. package/lib/linter/groupSchema.d.ts +1 -0
  169. package/lib/linter/groupSchema.js +1 -0
  170. package/lib/linter/groupSchema.js.map +1 -1
  171. package/lib/linter/lintProject.d.ts +1 -3
  172. package/lib/linter/lintProject.js +198 -68
  173. package/lib/linter/lintProject.js.map +1 -1
  174. package/lib/linter/schema.js +1 -0
  175. package/lib/linter/schema.js.map +1 -1
  176. package/lib/linter/segmentSchema.d.ts +1 -0
  177. package/lib/linter/segmentSchema.js +1 -0
  178. package/lib/linter/segmentSchema.js.map +1 -1
  179. package/lib/linter/targetSchema.d.ts +16 -0
  180. package/lib/linter/targetSchema.js +40 -0
  181. package/lib/linter/targetSchema.js.map +1 -0
  182. package/lib/linter/testSchema.d.ts +4 -3
  183. package/lib/linter/testSchema.js +9 -4
  184. package/lib/linter/testSchema.js.map +1 -1
  185. package/lib/list/index.d.ts +1 -0
  186. package/lib/list/index.js +131 -12
  187. package/lib/list/index.js.map +1 -1
  188. package/lib/list/matrix.js +3 -7
  189. package/lib/list/matrix.js.map +1 -1
  190. package/lib/promoter/index.d.ts +50 -0
  191. package/lib/promoter/index.js +880 -0
  192. package/lib/promoter/index.js.map +1 -0
  193. package/lib/sets.d.ts +11 -0
  194. package/lib/sets.js +81 -0
  195. package/lib/sets.js.map +1 -0
  196. package/lib/targeting.d.ts +14 -0
  197. package/lib/targeting.js +82 -0
  198. package/lib/targeting.js.map +1 -0
  199. package/lib/tester/cliFormat.d.ts +7 -1
  200. package/lib/tester/cliFormat.js +11 -2
  201. package/lib/tester/cliFormat.js.map +1 -1
  202. package/lib/tester/printTestResult.js +2 -2
  203. package/lib/tester/printTestResult.js.map +1 -1
  204. package/lib/tester/testFeature.d.ts +0 -1
  205. package/lib/tester/testFeature.js +8 -25
  206. package/lib/tester/testFeature.js.map +1 -1
  207. package/lib/tester/testProject.d.ts +2 -3
  208. package/lib/tester/testProject.js +93 -86
  209. package/lib/tester/testProject.js.map +1 -1
  210. package/lib/tester/testSegment.d.ts +1 -1
  211. package/lib/tester/testSegment.js +3 -21
  212. package/lib/tester/testSegment.js.map +1 -1
  213. package/lib/utils/git.js +45 -19
  214. package/lib/utils/git.js.map +1 -1
  215. package/package.json +12 -12
  216. package/src/assess-distribution/index.ts +72 -52
  217. package/src/benchmark/index.ts +124 -91
  218. package/src/builder/{buildScopedConditions.spec.ts → applyContextToConditions.spec.ts} +160 -142
  219. package/src/builder/{buildScopedConditions.ts → applyContextToConditions.ts} +19 -14
  220. package/src/builder/{buildScopedDatafile.spec.ts → applyContextToDatafile.spec.ts} +141 -81
  221. package/src/builder/{buildScopedDatafile.ts → applyContextToDatafile.ts} +33 -33
  222. package/src/builder/{buildScopedSegments.spec.ts → applyContextToSegments.spec.ts} +171 -151
  223. package/src/builder/{buildScopedSegments.ts → applyContextToSegments.ts} +26 -14
  224. package/src/builder/buildDatafile.spec.ts +192 -14
  225. package/src/builder/buildDatafile.ts +74 -64
  226. package/src/builder/buildProject.ts +171 -86
  227. package/src/builder/buildRuntimeDatafiles.ts +57 -0
  228. package/src/builder/index.ts +0 -1
  229. package/src/cli/cli.ts +0 -5
  230. package/src/cli/plugins.ts +7 -2
  231. package/src/config/projectConfig.spec.ts +171 -8
  232. package/src/config/projectConfig.ts +100 -28
  233. package/src/datasource/adapter.ts +8 -23
  234. package/src/datasource/datasource.ts +50 -21
  235. package/src/datasource/filesystemAdapter.spec.ts +133 -74
  236. package/src/datasource/filesystemAdapter.ts +93 -227
  237. package/src/evaluate/index.ts +109 -97
  238. package/src/find-duplicate-segments/index.ts +34 -16
  239. package/src/find-usage/index.ts +89 -44
  240. package/src/generate-code/index.ts +36 -24
  241. package/src/generate-code/typescript.spec.ts +106 -0
  242. package/src/generate-code/typescript.ts +33 -398
  243. package/src/index.ts +2 -1
  244. package/src/info/index.ts +72 -16
  245. package/src/init/index.ts +1 -1
  246. package/src/linter/attributeSchema.ts +1 -0
  247. package/src/linter/conditionSchema.spec.ts +23 -3
  248. package/src/linter/conditionSchema.ts +5 -5
  249. package/src/linter/featureSchema.spec.ts +107 -18
  250. package/src/linter/featureSchema.ts +25 -28
  251. package/src/linter/groupSchema.ts +1 -0
  252. package/src/linter/lintProject.spec.ts +131 -92
  253. package/src/linter/lintProject.ts +233 -97
  254. package/src/linter/schema.ts +1 -0
  255. package/src/linter/segmentSchema.spec.ts +5 -3
  256. package/src/linter/segmentSchema.ts +1 -0
  257. package/src/linter/targetSchema.spec.ts +159 -0
  258. package/src/linter/targetSchema.ts +46 -0
  259. package/src/linter/testSchema.spec.ts +70 -5
  260. package/src/linter/testSchema.ts +12 -10
  261. package/src/list/index.spec.ts +135 -0
  262. package/src/list/index.ts +188 -12
  263. package/src/list/matrix.ts +3 -8
  264. package/src/promoter/index.spec.ts +617 -0
  265. package/src/promoter/index.ts +1266 -0
  266. package/src/sets.ts +76 -0
  267. package/src/targeting.spec.ts +74 -0
  268. package/src/targeting.ts +106 -0
  269. package/src/tester/cliFormat.ts +11 -1
  270. package/src/tester/printTestResult.ts +12 -3
  271. package/src/tester/testFeature.ts +10 -36
  272. package/src/tester/testProject.spec.ts +50 -0
  273. package/src/tester/testProject.ts +89 -140
  274. package/src/tester/testSegment.ts +5 -22
  275. package/src/utils/git.ts +13 -21
  276. package/tsconfig.cjs.json +5 -1
  277. package/tsconfig.typecheck.json +9 -0
  278. package/coverage/lcov-report/builder/convertToV1.ts.html +0 -583
  279. package/lib/builder/allocator.spec.d.ts +0 -1
  280. package/lib/builder/allocator.spec.js +0 -85
  281. package/lib/builder/allocator.spec.js.map +0 -1
  282. package/lib/builder/buildDatafile.spec.d.ts +0 -1
  283. package/lib/builder/buildDatafile.spec.js +0 -233
  284. package/lib/builder/buildDatafile.spec.js.map +0 -1
  285. package/lib/builder/buildScopedConditions.d.ts +0 -5
  286. package/lib/builder/buildScopedConditions.js.map +0 -1
  287. package/lib/builder/buildScopedConditions.spec.d.ts +0 -1
  288. package/lib/builder/buildScopedConditions.spec.js +0 -2167
  289. package/lib/builder/buildScopedConditions.spec.js.map +0 -1
  290. package/lib/builder/buildScopedDatafile.d.ts +0 -2
  291. package/lib/builder/buildScopedDatafile.js.map +0 -1
  292. package/lib/builder/buildScopedDatafile.spec.d.ts +0 -1
  293. package/lib/builder/buildScopedDatafile.spec.js +0 -2277
  294. package/lib/builder/buildScopedDatafile.spec.js.map +0 -1
  295. package/lib/builder/buildScopedSegments.d.ts +0 -5
  296. package/lib/builder/buildScopedSegments.js.map +0 -1
  297. package/lib/builder/buildScopedSegments.spec.d.ts +0 -1
  298. package/lib/builder/buildScopedSegments.spec.js +0 -1062
  299. package/lib/builder/buildScopedSegments.spec.js.map +0 -1
  300. package/lib/builder/convertToV1.d.ts +0 -10
  301. package/lib/builder/convertToV1.js +0 -119
  302. package/lib/builder/convertToV1.js.map +0 -1
  303. package/lib/builder/mutateVariables.spec.d.ts +0 -1
  304. package/lib/builder/mutateVariables.spec.js +0 -1074
  305. package/lib/builder/mutateVariables.spec.js.map +0 -1
  306. package/lib/builder/mutator.spec.d.ts +0 -1
  307. package/lib/builder/mutator.spec.js +0 -368
  308. package/lib/builder/mutator.spec.js.map +0 -1
  309. package/lib/builder/revision.spec.d.ts +0 -1
  310. package/lib/builder/revision.spec.js +0 -21
  311. package/lib/builder/revision.spec.js.map +0 -1
  312. package/lib/builder/traffic.spec.d.ts +0 -1
  313. package/lib/builder/traffic.spec.js +0 -872
  314. package/lib/builder/traffic.spec.js.map +0 -1
  315. package/lib/config/projectConfig.spec.d.ts +0 -1
  316. package/lib/config/projectConfig.spec.js +0 -24
  317. package/lib/config/projectConfig.spec.js.map +0 -1
  318. package/lib/datasource/filesystemAdapter.spec.d.ts +0 -1
  319. package/lib/datasource/filesystemAdapter.spec.js +0 -88
  320. package/lib/datasource/filesystemAdapter.spec.js.map +0 -1
  321. package/lib/generate-code/typescript.spec.d.ts +0 -1
  322. package/lib/generate-code/typescript.spec.js +0 -78
  323. package/lib/generate-code/typescript.spec.js.map +0 -1
  324. package/lib/linter/attributeSchema.spec.d.ts +0 -1
  325. package/lib/linter/attributeSchema.spec.js +0 -266
  326. package/lib/linter/attributeSchema.spec.js.map +0 -1
  327. package/lib/linter/conditionSchema.spec.d.ts +0 -1
  328. package/lib/linter/conditionSchema.spec.js +0 -380
  329. package/lib/linter/conditionSchema.spec.js.map +0 -1
  330. package/lib/linter/featureSchema.spec.d.ts +0 -1
  331. package/lib/linter/featureSchema.spec.js +0 -1552
  332. package/lib/linter/featureSchema.spec.js.map +0 -1
  333. package/lib/linter/lintProject.spec.d.ts +0 -1
  334. package/lib/linter/lintProject.spec.js +0 -211
  335. package/lib/linter/lintProject.spec.js.map +0 -1
  336. package/lib/linter/mutationNotation.spec.d.ts +0 -1
  337. package/lib/linter/mutationNotation.spec.js +0 -598
  338. package/lib/linter/mutationNotation.spec.js.map +0 -1
  339. package/lib/linter/schema.spec.d.ts +0 -1
  340. package/lib/linter/schema.spec.js +0 -535
  341. package/lib/linter/schema.spec.js.map +0 -1
  342. package/lib/linter/segmentSchema.spec.d.ts +0 -1
  343. package/lib/linter/segmentSchema.spec.js +0 -303
  344. package/lib/linter/segmentSchema.spec.js.map +0 -1
  345. package/lib/linter/testSchema.spec.d.ts +0 -1
  346. package/lib/linter/testSchema.spec.js +0 -162
  347. package/lib/linter/testSchema.spec.js.map +0 -1
  348. package/lib/list/matrix.spec.d.ts +0 -1
  349. package/lib/list/matrix.spec.js +0 -42
  350. package/lib/list/matrix.spec.js.map +0 -1
  351. package/lib/site/exportSite.d.ts +0 -2
  352. package/lib/site/exportSite.js +0 -30
  353. package/lib/site/exportSite.js.map +0 -1
  354. package/lib/site/generateHistory.d.ts +0 -3
  355. package/lib/site/generateHistory.js +0 -31
  356. package/lib/site/generateHistory.js.map +0 -1
  357. package/lib/site/generateSiteSearchIndex.d.ts +0 -4
  358. package/lib/site/generateSiteSearchIndex.js +0 -195
  359. package/lib/site/generateSiteSearchIndex.js.map +0 -1
  360. package/lib/site/getLastModifiedFromHistory.d.ts +0 -2
  361. package/lib/site/getLastModifiedFromHistory.js +0 -18
  362. package/lib/site/getLastModifiedFromHistory.js.map +0 -1
  363. package/lib/site/getOwnerAndRepoFromUrl.d.ts +0 -4
  364. package/lib/site/getOwnerAndRepoFromUrl.js +0 -20
  365. package/lib/site/getOwnerAndRepoFromUrl.js.map +0 -1
  366. package/lib/site/getRelativePaths.d.ts +0 -6
  367. package/lib/site/getRelativePaths.js +0 -15
  368. package/lib/site/getRelativePaths.js.map +0 -1
  369. package/lib/site/getRepoDetails.d.ts +0 -8
  370. package/lib/site/getRepoDetails.js +0 -48
  371. package/lib/site/getRepoDetails.js.map +0 -1
  372. package/lib/site/index.d.ts +0 -2
  373. package/lib/site/index.js +0 -43
  374. package/lib/site/index.js.map +0 -1
  375. package/lib/site/serveSite.d.ts +0 -2
  376. package/lib/site/serveSite.js +0 -54
  377. package/lib/site/serveSite.js.map +0 -1
  378. package/lib/tester/helpers.spec.d.ts +0 -1
  379. package/lib/tester/helpers.spec.js +0 -115
  380. package/lib/tester/helpers.spec.js.map +0 -1
  381. package/src/builder/convertToV1.ts +0 -166
  382. package/src/site/exportSite.ts +0 -42
  383. package/src/site/generateHistory.ts +0 -40
  384. package/src/site/generateSiteSearchIndex.ts +0 -258
  385. package/src/site/getLastModifiedFromHistory.ts +0 -21
  386. package/src/site/getOwnerAndRepoFromUrl.ts +0 -17
  387. package/src/site/getRelativePaths.ts +0 -24
  388. package/src/site/getRepoDetails.ts +0 -62
  389. package/src/site/index.ts +0 -46
  390. 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
+ });