@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.
- package/CHANGELOG.md +8 -0
- package/coverage/clover.xml +4991 -3130
- package/coverage/coverage-final.json +63 -39
- package/coverage/lcov-report/block-navigation.js +1 -1
- package/coverage/lcov-report/index.html +248 -98
- package/coverage/lcov-report/sorter.js +21 -7
- package/coverage/lcov-report/src/assess-distribution/index.html +116 -0
- package/coverage/lcov-report/src/assess-distribution/index.ts.html +754 -0
- package/coverage/lcov-report/{list → src/benchmark}/index.html +30 -30
- package/coverage/lcov-report/src/benchmark/index.ts.html +793 -0
- package/coverage/lcov-report/{builder → src/builder}/allocator.ts.html +38 -38
- package/coverage/lcov-report/{builder/buildScopedConditions.ts.html → src/builder/applyContextToConditions.ts.html} +80 -65
- package/coverage/lcov-report/{builder/buildScopedDatafile.ts.html → src/builder/applyContextToDatafile.ts.html} +92 -92
- package/coverage/lcov-report/{builder/buildScopedSegments.ts.html → src/builder/applyContextToSegments.ts.html} +103 -67
- package/coverage/lcov-report/{builder → src/builder}/buildDatafile.ts.html +336 -306
- package/coverage/lcov-report/src/builder/buildProject.ts.html +1117 -0
- package/coverage/lcov-report/src/builder/buildRuntimeDatafiles.ts.html +256 -0
- package/coverage/lcov-report/{builder → src/builder}/getFeatureRanges.ts.html +22 -22
- package/coverage/lcov-report/{builder → src/builder}/hashes.ts.html +50 -50
- package/coverage/lcov-report/{builder → src/builder}/index.html +114 -84
- package/coverage/lcov-report/{tester/cliFormat.ts.html → src/builder/index.ts.html} +17 -29
- package/coverage/lcov-report/{builder → src/builder}/mutateVariables.ts.html +55 -55
- package/coverage/lcov-report/{builder → src/builder}/mutator.ts.html +118 -118
- package/coverage/lcov-report/{builder → src/builder}/revision.ts.html +15 -15
- package/coverage/lcov-report/{builder → src/builder}/traffic.ts.html +86 -86
- package/coverage/lcov-report/src/cli/cli.ts.html +400 -0
- package/coverage/lcov-report/{tester → src/cli}/index.html +41 -41
- package/coverage/lcov-report/src/cli/plugins.ts.html +214 -0
- package/coverage/lcov-report/{config → src/config}/index.html +31 -31
- package/coverage/lcov-report/{config → src/config}/index.ts.html +12 -12
- package/coverage/lcov-report/{config → src/config}/projectConfig.ts.html +312 -96
- package/coverage/lcov-report/{datasource → src/datasource}/adapter.ts.html +28 -73
- package/coverage/lcov-report/{datasource → src/datasource}/datasource.ts.html +191 -104
- package/coverage/lcov-report/{datasource → src/datasource}/filesystemAdapter.ts.html +233 -635
- package/coverage/lcov-report/{datasource → src/datasource}/index.html +48 -48
- package/coverage/lcov-report/{datasource → src/datasource}/index.ts.html +14 -14
- package/coverage/lcov-report/src/evaluate/index.html +116 -0
- package/coverage/lcov-report/src/evaluate/index.ts.html +814 -0
- package/coverage/lcov-report/src/find-duplicate-segments/findDuplicateSegments.ts.html +313 -0
- package/coverage/lcov-report/src/find-duplicate-segments/index.html +131 -0
- package/coverage/lcov-report/src/find-duplicate-segments/index.ts.html +307 -0
- package/coverage/lcov-report/src/find-usage/index.html +116 -0
- package/coverage/lcov-report/src/find-usage/index.ts.html +1780 -0
- package/coverage/lcov-report/src/generate-code/index.html +131 -0
- package/coverage/lcov-report/src/generate-code/index.ts.html +403 -0
- package/coverage/lcov-report/{generate-code → src/generate-code}/typescript.ts.html +266 -1361
- package/coverage/lcov-report/{generate-code → src}/index.html +38 -23
- package/coverage/lcov-report/src/info/index.html +116 -0
- package/coverage/lcov-report/src/info/index.ts.html +472 -0
- package/coverage/lcov-report/src/init/index.html +116 -0
- package/coverage/lcov-report/src/init/index.ts.html +280 -0
- package/coverage/lcov-report/{linter → src/linter}/attributeSchema.ts.html +160 -157
- package/coverage/lcov-report/{linter → src/linter}/checkCircularDependency.ts.html +25 -25
- package/coverage/lcov-report/{linter → src/linter}/checkPercentageExceedingSlot.ts.html +42 -42
- package/coverage/lcov-report/{linter → src/linter}/conditionSchema.ts.html +207 -207
- package/coverage/lcov-report/{linter → src/linter}/featureSchema.ts.html +462 -471
- package/coverage/lcov-report/{linter → src/linter}/groupSchema.ts.html +25 -22
- package/coverage/lcov-report/{linter → src/linter}/index.html +129 -99
- package/coverage/lcov-report/src/linter/index.ts.html +88 -0
- package/coverage/lcov-report/{linter → src/linter}/lintProject.ts.html +740 -332
- package/coverage/lcov-report/{linter → src/linter}/mutationNotation.ts.html +130 -130
- package/coverage/lcov-report/{linter → src/linter}/printError.ts.html +27 -27
- package/coverage/lcov-report/{linter → src/linter}/schema.ts.html +153 -150
- package/coverage/lcov-report/{linter → src/linter}/segmentSchema.ts.html +18 -15
- package/coverage/lcov-report/src/linter/targetSchema.ts.html +223 -0
- package/coverage/lcov-report/{linter → src/linter}/testSchema.ts.html +54 -48
- package/coverage/lcov-report/{linter → src/linter}/zodHelpers.ts.html +17 -17
- package/coverage/lcov-report/src/list/index.html +131 -0
- package/coverage/lcov-report/src/list/index.ts.html +2065 -0
- package/coverage/lcov-report/{list → src/list}/matrix.ts.html +43 -58
- package/coverage/lcov-report/src/promoter/index.html +116 -0
- package/coverage/lcov-report/src/promoter/index.ts.html +3883 -0
- package/coverage/lcov-report/src/sets.ts.html +313 -0
- package/coverage/lcov-report/src/targeting.ts.html +403 -0
- package/coverage/lcov-report/src/tester/cliFormat.ts.html +133 -0
- package/coverage/lcov-report/{tester → src/tester}/helpers.ts.html +15 -15
- package/coverage/lcov-report/src/tester/index.html +206 -0
- package/coverage/lcov-report/src/tester/prettyDuration.ts.html +187 -0
- package/coverage/lcov-report/src/tester/printTestResult.ts.html +337 -0
- package/coverage/lcov-report/src/tester/testFeature.ts.html +1186 -0
- package/coverage/lcov-report/src/tester/testProject.ts.html +1018 -0
- package/coverage/lcov-report/src/tester/testSegment.ts.html +316 -0
- package/coverage/lcov-report/{utils → src/utils}/extractKeys.ts.html +68 -68
- package/coverage/lcov-report/{utils → src/utils}/git.ts.html +44 -68
- package/coverage/lcov-report/{utils → src/utils}/index.html +44 -44
- package/coverage/lcov-report/{utils → src/utils}/index.ts.html +14 -14
- package/coverage/lcov-report/{utils → src/utils}/pretty.ts.html +20 -20
- package/coverage/lcov.info +10121 -5723
- package/jest.config.js +13 -4
- package/lib/assess-distribution/index.d.ts +1 -1
- package/lib/assess-distribution/index.js +59 -40
- package/lib/assess-distribution/index.js.map +1 -1
- package/lib/benchmark/index.d.ts +8 -5
- package/lib/benchmark/index.js +82 -65
- package/lib/benchmark/index.js.map +1 -1
- package/lib/builder/applyContextToConditions.d.ts +5 -0
- package/lib/builder/{buildScopedConditions.js → applyContextToConditions.js} +19 -14
- package/lib/builder/applyContextToConditions.js.map +1 -0
- package/lib/builder/applyContextToDatafile.d.ts +2 -0
- package/lib/builder/{buildScopedDatafile.js → applyContextToDatafile.js} +26 -26
- package/lib/builder/applyContextToDatafile.js.map +1 -0
- package/lib/builder/applyContextToSegments.d.ts +5 -0
- package/lib/builder/{buildScopedSegments.js → applyContextToSegments.js} +19 -14
- package/lib/builder/applyContextToSegments.js.map +1 -0
- package/lib/builder/buildDatafile.d.ts +9 -6
- package/lib/builder/buildDatafile.js +86 -51
- package/lib/builder/buildDatafile.js.map +1 -1
- package/lib/builder/buildProject.d.ts +17 -3
- package/lib/builder/buildProject.js +151 -59
- package/lib/builder/buildProject.js.map +1 -1
- package/lib/builder/buildRuntimeDatafiles.d.ts +12 -0
- package/lib/builder/buildRuntimeDatafiles.js +35 -0
- package/lib/builder/buildRuntimeDatafiles.js.map +1 -0
- package/lib/builder/getFeatureRanges.js +34 -1
- package/lib/builder/getFeatureRanges.js.map +1 -1
- package/lib/builder/hashes.js +34 -1
- package/lib/builder/hashes.js.map +1 -1
- package/lib/builder/index.d.ts +0 -1
- package/lib/builder/index.js +0 -1
- package/lib/builder/index.js.map +1 -1
- package/lib/cli/cli.js +0 -4
- package/lib/cli/cli.js.map +1 -1
- package/lib/cli/plugins.js +7 -2
- package/lib/cli/plugins.js.map +1 -1
- package/lib/config/projectConfig.d.ts +17 -17
- package/lib/config/projectConfig.js +101 -15
- package/lib/config/projectConfig.js.map +1 -1
- package/lib/datasource/adapter.d.ts +7 -9
- package/lib/datasource/adapter.js +0 -7
- package/lib/datasource/adapter.js.map +1 -1
- package/lib/datasource/datasource.d.ts +15 -5
- package/lib/datasource/datasource.js +37 -9
- package/lib/datasource/datasource.js.map +1 -1
- package/lib/datasource/filesystemAdapter.d.ts +4 -10
- package/lib/datasource/filesystemAdapter.js +111 -149
- package/lib/datasource/filesystemAdapter.js.map +1 -1
- package/lib/evaluate/index.d.ts +1 -7
- package/lib/evaluate/index.js +81 -67
- package/lib/evaluate/index.js.map +1 -1
- package/lib/find-duplicate-segments/findDuplicateSegments.js +34 -1
- package/lib/find-duplicate-segments/findDuplicateSegments.js.map +1 -1
- package/lib/find-duplicate-segments/index.js +21 -13
- package/lib/find-duplicate-segments/index.js.map +1 -1
- package/lib/find-usage/index.js +57 -41
- package/lib/find-usage/index.js.map +1 -1
- package/lib/generate-code/index.d.ts +1 -1
- package/lib/generate-code/index.js +64 -21
- package/lib/generate-code/index.js.map +1 -1
- package/lib/generate-code/typescript.d.ts +1 -1
- package/lib/generate-code/typescript.js +59 -333
- package/lib/generate-code/typescript.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/info/index.js +57 -16
- package/lib/info/index.js.map +1 -1
- package/lib/init/index.d.ts +2 -2
- package/lib/init/index.js +39 -3
- package/lib/init/index.js.map +1 -1
- package/lib/linter/attributeSchema.d.ts +1 -0
- package/lib/linter/attributeSchema.js +1 -0
- package/lib/linter/attributeSchema.js.map +1 -1
- package/lib/linter/conditionSchema.js +5 -5
- package/lib/linter/conditionSchema.js.map +1 -1
- package/lib/linter/featureSchema.d.ts +4 -1
- package/lib/linter/featureSchema.js +15 -13
- package/lib/linter/featureSchema.js.map +1 -1
- package/lib/linter/groupSchema.d.ts +1 -0
- package/lib/linter/groupSchema.js +1 -0
- package/lib/linter/groupSchema.js.map +1 -1
- package/lib/linter/lintProject.d.ts +1 -3
- package/lib/linter/lintProject.js +198 -68
- package/lib/linter/lintProject.js.map +1 -1
- package/lib/linter/schema.js +1 -0
- package/lib/linter/schema.js.map +1 -1
- package/lib/linter/segmentSchema.d.ts +1 -0
- package/lib/linter/segmentSchema.js +1 -0
- package/lib/linter/segmentSchema.js.map +1 -1
- package/lib/linter/targetSchema.d.ts +16 -0
- package/lib/linter/targetSchema.js +40 -0
- package/lib/linter/targetSchema.js.map +1 -0
- package/lib/linter/testSchema.d.ts +4 -3
- package/lib/linter/testSchema.js +9 -4
- package/lib/linter/testSchema.js.map +1 -1
- package/lib/list/index.d.ts +1 -0
- package/lib/list/index.js +131 -12
- package/lib/list/index.js.map +1 -1
- package/lib/list/matrix.js +3 -7
- package/lib/list/matrix.js.map +1 -1
- package/lib/promoter/index.d.ts +50 -0
- package/lib/promoter/index.js +880 -0
- package/lib/promoter/index.js.map +1 -0
- package/lib/sets.d.ts +11 -0
- package/lib/sets.js +81 -0
- package/lib/sets.js.map +1 -0
- package/lib/targeting.d.ts +14 -0
- package/lib/targeting.js +82 -0
- package/lib/targeting.js.map +1 -0
- package/lib/tester/cliFormat.d.ts +7 -1
- package/lib/tester/cliFormat.js +11 -2
- package/lib/tester/cliFormat.js.map +1 -1
- package/lib/tester/printTestResult.js +2 -2
- package/lib/tester/printTestResult.js.map +1 -1
- package/lib/tester/testFeature.d.ts +0 -1
- package/lib/tester/testFeature.js +8 -25
- package/lib/tester/testFeature.js.map +1 -1
- package/lib/tester/testProject.d.ts +2 -3
- package/lib/tester/testProject.js +93 -86
- package/lib/tester/testProject.js.map +1 -1
- package/lib/tester/testSegment.d.ts +1 -1
- package/lib/tester/testSegment.js +3 -21
- package/lib/tester/testSegment.js.map +1 -1
- package/lib/utils/git.js +45 -19
- package/lib/utils/git.js.map +1 -1
- package/package.json +12 -12
- package/src/assess-distribution/index.ts +72 -52
- package/src/benchmark/index.ts +124 -91
- package/src/builder/{buildScopedConditions.spec.ts → applyContextToConditions.spec.ts} +160 -142
- package/src/builder/{buildScopedConditions.ts → applyContextToConditions.ts} +19 -14
- package/src/builder/{buildScopedDatafile.spec.ts → applyContextToDatafile.spec.ts} +141 -81
- package/src/builder/{buildScopedDatafile.ts → applyContextToDatafile.ts} +33 -33
- package/src/builder/{buildScopedSegments.spec.ts → applyContextToSegments.spec.ts} +171 -151
- package/src/builder/{buildScopedSegments.ts → applyContextToSegments.ts} +26 -14
- package/src/builder/buildDatafile.spec.ts +192 -14
- package/src/builder/buildDatafile.ts +74 -64
- package/src/builder/buildProject.ts +171 -86
- package/src/builder/buildRuntimeDatafiles.ts +57 -0
- package/src/builder/index.ts +0 -1
- package/src/cli/cli.ts +0 -5
- package/src/cli/plugins.ts +7 -2
- package/src/config/projectConfig.spec.ts +171 -8
- package/src/config/projectConfig.ts +100 -28
- package/src/datasource/adapter.ts +8 -23
- package/src/datasource/datasource.ts +50 -21
- package/src/datasource/filesystemAdapter.spec.ts +133 -74
- package/src/datasource/filesystemAdapter.ts +93 -227
- package/src/evaluate/index.ts +109 -97
- package/src/find-duplicate-segments/index.ts +34 -16
- package/src/find-usage/index.ts +89 -44
- package/src/generate-code/index.ts +36 -24
- package/src/generate-code/typescript.spec.ts +106 -0
- package/src/generate-code/typescript.ts +33 -398
- package/src/index.ts +2 -1
- package/src/info/index.ts +72 -16
- package/src/init/index.ts +1 -1
- package/src/linter/attributeSchema.ts +1 -0
- package/src/linter/conditionSchema.spec.ts +23 -3
- package/src/linter/conditionSchema.ts +5 -5
- package/src/linter/featureSchema.spec.ts +107 -18
- package/src/linter/featureSchema.ts +25 -28
- package/src/linter/groupSchema.ts +1 -0
- package/src/linter/lintProject.spec.ts +131 -92
- package/src/linter/lintProject.ts +233 -97
- package/src/linter/schema.ts +1 -0
- package/src/linter/segmentSchema.spec.ts +5 -3
- package/src/linter/segmentSchema.ts +1 -0
- package/src/linter/targetSchema.spec.ts +159 -0
- package/src/linter/targetSchema.ts +46 -0
- package/src/linter/testSchema.spec.ts +70 -5
- package/src/linter/testSchema.ts +12 -10
- package/src/list/index.spec.ts +135 -0
- package/src/list/index.ts +188 -12
- package/src/list/matrix.ts +3 -8
- package/src/promoter/index.spec.ts +617 -0
- package/src/promoter/index.ts +1266 -0
- package/src/sets.ts +76 -0
- package/src/targeting.spec.ts +74 -0
- package/src/targeting.ts +106 -0
- package/src/tester/cliFormat.ts +11 -1
- package/src/tester/printTestResult.ts +12 -3
- package/src/tester/testFeature.ts +10 -36
- package/src/tester/testProject.spec.ts +50 -0
- package/src/tester/testProject.ts +89 -140
- package/src/tester/testSegment.ts +5 -22
- package/src/utils/git.ts +13 -21
- package/tsconfig.cjs.json +5 -1
- package/tsconfig.typecheck.json +9 -0
- package/coverage/lcov-report/builder/convertToV1.ts.html +0 -583
- package/lib/builder/allocator.spec.d.ts +0 -1
- package/lib/builder/allocator.spec.js +0 -85
- package/lib/builder/allocator.spec.js.map +0 -1
- package/lib/builder/buildDatafile.spec.d.ts +0 -1
- package/lib/builder/buildDatafile.spec.js +0 -233
- package/lib/builder/buildDatafile.spec.js.map +0 -1
- package/lib/builder/buildScopedConditions.d.ts +0 -5
- package/lib/builder/buildScopedConditions.js.map +0 -1
- package/lib/builder/buildScopedConditions.spec.d.ts +0 -1
- package/lib/builder/buildScopedConditions.spec.js +0 -2167
- package/lib/builder/buildScopedConditions.spec.js.map +0 -1
- package/lib/builder/buildScopedDatafile.d.ts +0 -2
- package/lib/builder/buildScopedDatafile.js.map +0 -1
- package/lib/builder/buildScopedDatafile.spec.d.ts +0 -1
- package/lib/builder/buildScopedDatafile.spec.js +0 -2277
- package/lib/builder/buildScopedDatafile.spec.js.map +0 -1
- package/lib/builder/buildScopedSegments.d.ts +0 -5
- package/lib/builder/buildScopedSegments.js.map +0 -1
- package/lib/builder/buildScopedSegments.spec.d.ts +0 -1
- package/lib/builder/buildScopedSegments.spec.js +0 -1062
- package/lib/builder/buildScopedSegments.spec.js.map +0 -1
- package/lib/builder/convertToV1.d.ts +0 -10
- package/lib/builder/convertToV1.js +0 -119
- package/lib/builder/convertToV1.js.map +0 -1
- package/lib/builder/mutateVariables.spec.d.ts +0 -1
- package/lib/builder/mutateVariables.spec.js +0 -1074
- package/lib/builder/mutateVariables.spec.js.map +0 -1
- package/lib/builder/mutator.spec.d.ts +0 -1
- package/lib/builder/mutator.spec.js +0 -368
- package/lib/builder/mutator.spec.js.map +0 -1
- package/lib/builder/revision.spec.d.ts +0 -1
- package/lib/builder/revision.spec.js +0 -21
- package/lib/builder/revision.spec.js.map +0 -1
- package/lib/builder/traffic.spec.d.ts +0 -1
- package/lib/builder/traffic.spec.js +0 -872
- package/lib/builder/traffic.spec.js.map +0 -1
- package/lib/config/projectConfig.spec.d.ts +0 -1
- package/lib/config/projectConfig.spec.js +0 -24
- package/lib/config/projectConfig.spec.js.map +0 -1
- package/lib/datasource/filesystemAdapter.spec.d.ts +0 -1
- package/lib/datasource/filesystemAdapter.spec.js +0 -88
- package/lib/datasource/filesystemAdapter.spec.js.map +0 -1
- package/lib/generate-code/typescript.spec.d.ts +0 -1
- package/lib/generate-code/typescript.spec.js +0 -78
- package/lib/generate-code/typescript.spec.js.map +0 -1
- package/lib/linter/attributeSchema.spec.d.ts +0 -1
- package/lib/linter/attributeSchema.spec.js +0 -266
- package/lib/linter/attributeSchema.spec.js.map +0 -1
- package/lib/linter/conditionSchema.spec.d.ts +0 -1
- package/lib/linter/conditionSchema.spec.js +0 -380
- package/lib/linter/conditionSchema.spec.js.map +0 -1
- package/lib/linter/featureSchema.spec.d.ts +0 -1
- package/lib/linter/featureSchema.spec.js +0 -1552
- package/lib/linter/featureSchema.spec.js.map +0 -1
- package/lib/linter/lintProject.spec.d.ts +0 -1
- package/lib/linter/lintProject.spec.js +0 -211
- package/lib/linter/lintProject.spec.js.map +0 -1
- package/lib/linter/mutationNotation.spec.d.ts +0 -1
- package/lib/linter/mutationNotation.spec.js +0 -598
- package/lib/linter/mutationNotation.spec.js.map +0 -1
- package/lib/linter/schema.spec.d.ts +0 -1
- package/lib/linter/schema.spec.js +0 -535
- package/lib/linter/schema.spec.js.map +0 -1
- package/lib/linter/segmentSchema.spec.d.ts +0 -1
- package/lib/linter/segmentSchema.spec.js +0 -303
- package/lib/linter/segmentSchema.spec.js.map +0 -1
- package/lib/linter/testSchema.spec.d.ts +0 -1
- package/lib/linter/testSchema.spec.js +0 -162
- package/lib/linter/testSchema.spec.js.map +0 -1
- package/lib/list/matrix.spec.d.ts +0 -1
- package/lib/list/matrix.spec.js +0 -42
- package/lib/list/matrix.spec.js.map +0 -1
- package/lib/site/exportSite.d.ts +0 -2
- package/lib/site/exportSite.js +0 -30
- package/lib/site/exportSite.js.map +0 -1
- package/lib/site/generateHistory.d.ts +0 -3
- package/lib/site/generateHistory.js +0 -31
- package/lib/site/generateHistory.js.map +0 -1
- package/lib/site/generateSiteSearchIndex.d.ts +0 -4
- package/lib/site/generateSiteSearchIndex.js +0 -195
- package/lib/site/generateSiteSearchIndex.js.map +0 -1
- package/lib/site/getLastModifiedFromHistory.d.ts +0 -2
- package/lib/site/getLastModifiedFromHistory.js +0 -18
- package/lib/site/getLastModifiedFromHistory.js.map +0 -1
- package/lib/site/getOwnerAndRepoFromUrl.d.ts +0 -4
- package/lib/site/getOwnerAndRepoFromUrl.js +0 -20
- package/lib/site/getOwnerAndRepoFromUrl.js.map +0 -1
- package/lib/site/getRelativePaths.d.ts +0 -6
- package/lib/site/getRelativePaths.js +0 -15
- package/lib/site/getRelativePaths.js.map +0 -1
- package/lib/site/getRepoDetails.d.ts +0 -8
- package/lib/site/getRepoDetails.js +0 -48
- package/lib/site/getRepoDetails.js.map +0 -1
- package/lib/site/index.d.ts +0 -2
- package/lib/site/index.js +0 -43
- package/lib/site/index.js.map +0 -1
- package/lib/site/serveSite.d.ts +0 -2
- package/lib/site/serveSite.js +0 -54
- package/lib/site/serveSite.js.map +0 -1
- package/lib/tester/helpers.spec.d.ts +0 -1
- package/lib/tester/helpers.spec.js +0 -115
- package/lib/tester/helpers.spec.js.map +0 -1
- package/src/builder/convertToV1.ts +0 -166
- package/src/site/exportSite.ts +0 -42
- package/src/site/generateHistory.ts +0 -40
- package/src/site/generateSiteSearchIndex.ts +0 -258
- package/src/site/getLastModifiedFromHistory.ts +0 -21
- package/src/site/getOwnerAndRepoFromUrl.ts +0 -17
- package/src/site/getRelativePaths.ts +0 -24
- package/src/site/getRepoDetails.ts +0 -62
- package/src/site/index.ts +0 -46
- package/src/site/serveSite.ts +0 -58
|
@@ -1,64 +1,62 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
2
|
|
|
3
|
-
import type { BucketBy
|
|
3
|
+
import type { BucketBy } from "@featurevisor/types";
|
|
4
4
|
import { Parser, parsers } from "@featurevisor/parsers";
|
|
5
5
|
|
|
6
6
|
import { FilesystemAdapter } from "../datasource/filesystemAdapter";
|
|
7
7
|
import type { Plugin } from "../cli";
|
|
8
|
-
import
|
|
8
|
+
import { CLI_COLOR_CYAN, CLI_FORMAT_BOLD, colorize } from "../tester/cliFormat";
|
|
9
9
|
|
|
10
10
|
export const FEATURES_DIRECTORY_NAME = "features";
|
|
11
11
|
export const SEGMENTS_DIRECTORY_NAME = "segments";
|
|
12
12
|
export const ATTRIBUTES_DIRECTORY_NAME = "attributes";
|
|
13
13
|
export const GROUPS_DIRECTORY_NAME = "groups";
|
|
14
14
|
export const SCHEMAS_DIRECTORY_NAME = "schemas";
|
|
15
|
+
export const TARGETS_DIRECTORY_NAME = "targets";
|
|
15
16
|
export const TESTS_DIRECTORY_NAME = "tests";
|
|
16
17
|
export const STATE_DIRECTORY_NAME = ".featurevisor";
|
|
17
18
|
export const DATAFILES_DIRECTORY_NAME = "datafiles";
|
|
18
19
|
export const DATAFILE_NAME_PATTERN = "featurevisor-%s.json";
|
|
19
20
|
export const REVISION_FILE_NAME = "REVISION";
|
|
20
|
-
export const
|
|
21
|
-
export const
|
|
21
|
+
export const CATALOG_DIRECTORY_NAME = "catalog";
|
|
22
|
+
export const SETS_DIRECTORY_NAME = "sets";
|
|
22
23
|
|
|
23
24
|
export const CONFIG_MODULE_NAME = "featurevisor.config.js";
|
|
24
25
|
export const ROOT_DIR_PLACEHOLDER = "<rootDir>";
|
|
25
26
|
|
|
26
|
-
export const
|
|
27
|
+
export const DEFAULT_NAMESPACE_CHARACTER = ".";
|
|
27
28
|
export const DEFAULT_TAGS = ["all"];
|
|
28
29
|
export const DEFAULT_BUCKET_BY_ATTRIBUTE = "userId";
|
|
30
|
+
export const DEFAULT_SETS = false;
|
|
29
31
|
|
|
30
32
|
export const DEFAULT_PRETTY_STATE = true;
|
|
31
33
|
export const DEFAULT_PRETTY_DATAFILE = false;
|
|
32
34
|
|
|
33
35
|
export const DEFAULT_PARSER: Parser = "yml";
|
|
34
36
|
|
|
35
|
-
export const SCHEMA_VERSION = "2"; // default schema version
|
|
36
|
-
|
|
37
|
-
export interface Scope {
|
|
38
|
-
name: string;
|
|
39
|
-
context: Context;
|
|
40
|
-
tag?: Tag;
|
|
41
|
-
tags?: BuildTags;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
37
|
export interface ProjectConfig {
|
|
38
|
+
promotionFlows?: Array<{
|
|
39
|
+
from: string;
|
|
40
|
+
to: string;
|
|
41
|
+
}>;
|
|
42
|
+
namespaceCharacter: string;
|
|
45
43
|
featuresDirectoryPath: string;
|
|
46
44
|
segmentsDirectoryPath: string;
|
|
47
45
|
attributesDirectoryPath: string;
|
|
48
46
|
groupsDirectoryPath: string;
|
|
49
47
|
schemasDirectoryPath: string;
|
|
48
|
+
targetsDirectoryPath: string;
|
|
50
49
|
testsDirectoryPath: string;
|
|
51
50
|
stateDirectoryPath: string;
|
|
52
51
|
datafilesDirectoryPath: string;
|
|
53
52
|
datafileNamePattern: string;
|
|
54
53
|
revisionFileName: string;
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
catalogDirectoryPath: string;
|
|
55
|
+
setsDirectoryPath: string;
|
|
57
56
|
|
|
58
|
-
environments
|
|
59
|
-
|
|
57
|
+
environments?: string[];
|
|
58
|
+
sets: boolean;
|
|
60
59
|
tags: string[];
|
|
61
|
-
scopes?: Scope[];
|
|
62
60
|
|
|
63
61
|
adapter: any; // @NOTE: type this properly later
|
|
64
62
|
plugins: Plugin[];
|
|
@@ -80,9 +78,11 @@ export interface ProjectConfig {
|
|
|
80
78
|
// rootDirectoryPath: path to the root directory of the project (without ending with a slash)
|
|
81
79
|
export function getProjectConfig(rootDirectoryPath: string): ProjectConfig {
|
|
82
80
|
const baseConfig: ProjectConfig = {
|
|
83
|
-
environments:
|
|
81
|
+
environments: undefined,
|
|
82
|
+
sets: DEFAULT_SETS,
|
|
83
|
+
promotionFlows: undefined,
|
|
84
|
+
namespaceCharacter: DEFAULT_NAMESPACE_CHARACTER,
|
|
84
85
|
tags: DEFAULT_TAGS,
|
|
85
|
-
scopes: [],
|
|
86
86
|
defaultBucketBy: "userId",
|
|
87
87
|
|
|
88
88
|
parser: DEFAULT_PARSER,
|
|
@@ -94,21 +94,21 @@ export function getProjectConfig(rootDirectoryPath: string): ProjectConfig {
|
|
|
94
94
|
adapter: FilesystemAdapter,
|
|
95
95
|
|
|
96
96
|
featuresDirectoryPath: path.join(rootDirectoryPath, FEATURES_DIRECTORY_NAME),
|
|
97
|
-
|
|
97
|
+
setsDirectoryPath: path.join(rootDirectoryPath, SETS_DIRECTORY_NAME),
|
|
98
98
|
segmentsDirectoryPath: path.join(rootDirectoryPath, SEGMENTS_DIRECTORY_NAME),
|
|
99
99
|
attributesDirectoryPath: path.join(rootDirectoryPath, ATTRIBUTES_DIRECTORY_NAME),
|
|
100
100
|
groupsDirectoryPath: path.join(rootDirectoryPath, GROUPS_DIRECTORY_NAME),
|
|
101
101
|
schemasDirectoryPath: path.join(rootDirectoryPath, SCHEMAS_DIRECTORY_NAME),
|
|
102
|
+
targetsDirectoryPath: path.join(rootDirectoryPath, TARGETS_DIRECTORY_NAME),
|
|
102
103
|
testsDirectoryPath: path.join(rootDirectoryPath, TESTS_DIRECTORY_NAME),
|
|
103
104
|
stateDirectoryPath: path.join(rootDirectoryPath, STATE_DIRECTORY_NAME),
|
|
104
105
|
datafilesDirectoryPath: path.join(rootDirectoryPath, DATAFILES_DIRECTORY_NAME),
|
|
105
106
|
datafileNamePattern: DATAFILE_NAME_PATTERN,
|
|
106
107
|
revisionFileName: REVISION_FILE_NAME,
|
|
107
|
-
|
|
108
|
+
catalogDirectoryPath: path.join(rootDirectoryPath, CATALOG_DIRECTORY_NAME),
|
|
108
109
|
|
|
109
110
|
enforceCatchAllRule: false,
|
|
110
111
|
plugins: [],
|
|
111
|
-
splitByEnvironment: false,
|
|
112
112
|
|
|
113
113
|
maxVariableStringLength: undefined,
|
|
114
114
|
maxVariableArrayStringifiedLength: undefined,
|
|
@@ -141,15 +141,83 @@ export function getProjectConfig(rootDirectoryPath: string): ProjectConfig {
|
|
|
141
141
|
finalConfig.parser = parsers[finalConfig.parser];
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
if (
|
|
144
|
+
if (typeof finalConfig.sets !== "boolean") {
|
|
145
|
+
throw new Error(`Invalid sets: ${finalConfig.sets}. It must be a boolean.`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (typeof finalConfig.environments !== "undefined") {
|
|
149
|
+
if (!Array.isArray(finalConfig.environments)) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Invalid environments: ${finalConfig.environments}. It must be an array of strings when defined.`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
finalConfig.environments.forEach((environment: unknown, index: number) => {
|
|
156
|
+
if (typeof environment !== "string") {
|
|
157
|
+
throw new Error(`Invalid environments[${index}]: ${environment}. It must be a string.`);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (
|
|
163
|
+
typeof finalConfig.namespaceCharacter !== "string" ||
|
|
164
|
+
finalConfig.namespaceCharacter.length === 0
|
|
165
|
+
) {
|
|
145
166
|
throw new Error(
|
|
146
|
-
|
|
167
|
+
`Invalid namespaceCharacter: ${finalConfig.namespaceCharacter}. It must be a non-empty string.`,
|
|
147
168
|
);
|
|
148
169
|
}
|
|
149
170
|
|
|
171
|
+
if (typeof finalConfig.promotionFlows !== "undefined") {
|
|
172
|
+
if (!Array.isArray(finalConfig.promotionFlows)) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`Invalid promotionFlows: ${finalConfig.promotionFlows}. It must be an array.`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
finalConfig.promotionFlows.forEach((entry: any, index: number) => {
|
|
179
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`Invalid promotionFlows[${index}]: ${entry}. Each entry must be an object with exactly "from" and "to" string fields.`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const keys = Object.keys(entry).sort();
|
|
186
|
+
|
|
187
|
+
if (keys.length !== 2 || keys[0] !== "from" || keys[1] !== "to") {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Invalid promotionFlows[${index}]: ${JSON.stringify(entry)}. Each entry must contain exactly "from" and "to".`,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (typeof entry.from !== "string" || typeof entry.to !== "string") {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`Invalid promotionFlows[${index}]: ${JSON.stringify(entry)}. "from" and "to" must be strings.`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
150
201
|
return finalConfig as ProjectConfig;
|
|
151
202
|
}
|
|
152
203
|
|
|
204
|
+
export function getProjectConfigForSet(projectConfig: ProjectConfig, set: string): ProjectConfig {
|
|
205
|
+
const setRootDirectoryPath = path.join(projectConfig.setsDirectoryPath, set);
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
...projectConfig,
|
|
209
|
+
featuresDirectoryPath: path.join(setRootDirectoryPath, FEATURES_DIRECTORY_NAME),
|
|
210
|
+
segmentsDirectoryPath: path.join(setRootDirectoryPath, SEGMENTS_DIRECTORY_NAME),
|
|
211
|
+
attributesDirectoryPath: path.join(setRootDirectoryPath, ATTRIBUTES_DIRECTORY_NAME),
|
|
212
|
+
groupsDirectoryPath: path.join(setRootDirectoryPath, GROUPS_DIRECTORY_NAME),
|
|
213
|
+
schemasDirectoryPath: path.join(setRootDirectoryPath, SCHEMAS_DIRECTORY_NAME),
|
|
214
|
+
targetsDirectoryPath: path.join(setRootDirectoryPath, TARGETS_DIRECTORY_NAME),
|
|
215
|
+
testsDirectoryPath: path.join(setRootDirectoryPath, TESTS_DIRECTORY_NAME),
|
|
216
|
+
stateDirectoryPath: path.join(projectConfig.stateDirectoryPath, SETS_DIRECTORY_NAME, set),
|
|
217
|
+
datafilesDirectoryPath: path.join(projectConfig.datafilesDirectoryPath, set),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
153
221
|
export interface ShowProjectConfigOptions {
|
|
154
222
|
json?: boolean;
|
|
155
223
|
pretty?: boolean;
|
|
@@ -167,7 +235,9 @@ export function showProjectConfig(
|
|
|
167
235
|
return;
|
|
168
236
|
}
|
|
169
237
|
|
|
170
|
-
console.log("
|
|
238
|
+
console.log("");
|
|
239
|
+
console.log(CLI_FORMAT_BOLD, "Project configuration");
|
|
240
|
+
console.log("");
|
|
171
241
|
|
|
172
242
|
const keys = Object.keys(projectConfig);
|
|
173
243
|
const longestKeyLength = keys.reduce((acc, key) => (key.length > acc ? key.length : acc), 0);
|
|
@@ -178,7 +248,9 @@ export function showProjectConfig(
|
|
|
178
248
|
continue;
|
|
179
249
|
}
|
|
180
250
|
|
|
181
|
-
console.log(
|
|
251
|
+
console.log(
|
|
252
|
+
` ${colorize(key.padEnd(longestKeyLength, " "), CLI_COLOR_CYAN)}: ${projectConfig[key]}`,
|
|
253
|
+
);
|
|
182
254
|
}
|
|
183
255
|
}
|
|
184
256
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
1
|
import type {
|
|
3
2
|
DatafileContent,
|
|
4
3
|
EnvironmentKey,
|
|
@@ -9,23 +8,21 @@ import type {
|
|
|
9
8
|
EntityType,
|
|
10
9
|
} from "@featurevisor/types";
|
|
11
10
|
|
|
12
|
-
import type { Scope } from "../config";
|
|
13
|
-
|
|
14
11
|
export interface DatafileOptions {
|
|
15
12
|
environment: EnvironmentKey | false;
|
|
16
|
-
|
|
17
|
-
scope?: Scope;
|
|
13
|
+
target?: string;
|
|
18
14
|
datafilesDir?: string;
|
|
19
15
|
}
|
|
20
16
|
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
environmentFilePaths: Record<string, string>;
|
|
17
|
+
export interface DatafileFile {
|
|
18
|
+
path: string;
|
|
19
|
+
size: number;
|
|
20
|
+
gzipSize: number;
|
|
26
21
|
}
|
|
27
22
|
|
|
28
23
|
export abstract class Adapter {
|
|
24
|
+
abstract listSets(): Promise<string[]>;
|
|
25
|
+
|
|
29
26
|
// entities
|
|
30
27
|
abstract listEntities(entityType: EntityType): Promise<string[]>;
|
|
31
28
|
abstract entityExists(entityType: EntityType, entityKey: string): Promise<boolean>;
|
|
@@ -41,6 +38,7 @@ export abstract class Adapter {
|
|
|
41
38
|
): Promise<void>;
|
|
42
39
|
|
|
43
40
|
// datafile
|
|
41
|
+
listDatafiles?(): Promise<DatafileFile[]>;
|
|
44
42
|
abstract readDatafile(options: DatafileOptions): Promise<DatafileContent>;
|
|
45
43
|
abstract writeDatafile(datafileContent: DatafileContent, options: DatafileOptions): Promise<void>;
|
|
46
44
|
|
|
@@ -55,17 +53,4 @@ export abstract class Adapter {
|
|
|
55
53
|
entityType?: EntityType,
|
|
56
54
|
entityKey?: string,
|
|
57
55
|
): Promise<Commit>;
|
|
58
|
-
|
|
59
|
-
// feature source helpers (used by lint/history attribution)
|
|
60
|
-
async getFeatureSourcePaths(_featureKey: string): Promise<FeatureSourcePaths | undefined> {
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async getFeaturePropertySourcePath(
|
|
65
|
-
_featureKey: string,
|
|
66
|
-
_property: FeatureEnvironmentProperty,
|
|
67
|
-
_environment?: string,
|
|
68
|
-
): Promise<string | undefined> {
|
|
69
|
-
return undefined;
|
|
70
|
-
}
|
|
71
56
|
}
|
|
@@ -13,29 +13,45 @@ import type {
|
|
|
13
13
|
EntityType,
|
|
14
14
|
SchemaKey,
|
|
15
15
|
Schema,
|
|
16
|
+
Target,
|
|
17
|
+
TargetKey,
|
|
16
18
|
} from "@featurevisor/types";
|
|
17
19
|
|
|
18
|
-
import { ProjectConfig } from "../config";
|
|
20
|
+
import { getProjectConfigForSet, ProjectConfig } from "../config";
|
|
19
21
|
import type { CustomParser } from "@featurevisor/parsers";
|
|
20
22
|
|
|
21
|
-
import {
|
|
22
|
-
Adapter,
|
|
23
|
-
DatafileOptions,
|
|
24
|
-
FeatureEnvironmentProperty,
|
|
25
|
-
FeatureSourcePaths,
|
|
26
|
-
} from "./adapter";
|
|
23
|
+
import { Adapter, DatafileFile, DatafileOptions } from "./adapter";
|
|
27
24
|
|
|
28
25
|
export class Datasource {
|
|
29
26
|
private adapter: Adapter;
|
|
27
|
+
private rootConfig: ProjectConfig;
|
|
30
28
|
|
|
31
29
|
constructor(
|
|
32
30
|
private config: ProjectConfig,
|
|
33
31
|
private rootDirectoryPath?: string,
|
|
32
|
+
private set?: string,
|
|
34
33
|
) {
|
|
35
|
-
this.
|
|
34
|
+
this.rootConfig = config;
|
|
35
|
+
this.config = set ? getProjectConfigForSet(this.rootConfig, set) : config;
|
|
36
|
+
this.adapter = new this.config.adapter(this.config, rootDirectoryPath);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getConfig() {
|
|
40
|
+
return this.config;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getSet() {
|
|
44
|
+
return this.set;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
forSet(set: string) {
|
|
48
|
+
return new Datasource(this.rootConfig, this.rootDirectoryPath, set);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
listSets() {
|
|
52
|
+
return this.adapter.listSets();
|
|
36
53
|
}
|
|
37
54
|
|
|
38
|
-
// @NOTE: only site generator needs it, find a way to get it out of here later
|
|
39
55
|
getExtension() {
|
|
40
56
|
return (this.config.parser as CustomParser).extension;
|
|
41
57
|
}
|
|
@@ -65,6 +81,10 @@ export class Datasource {
|
|
|
65
81
|
/**
|
|
66
82
|
* Datafile
|
|
67
83
|
*/
|
|
84
|
+
listDatafiles(): Promise<DatafileFile[]> {
|
|
85
|
+
return this.adapter.listDatafiles ? this.adapter.listDatafiles() : Promise.resolve([]);
|
|
86
|
+
}
|
|
87
|
+
|
|
68
88
|
readDatafile(options: DatafileOptions) {
|
|
69
89
|
return this.adapter.readDatafile(options);
|
|
70
90
|
}
|
|
@@ -98,18 +118,6 @@ export class Datasource {
|
|
|
98
118
|
return this.adapter.deleteEntity("feature", featureKey);
|
|
99
119
|
}
|
|
100
120
|
|
|
101
|
-
getFeatureSourcePaths(featureKey: FeatureKey): Promise<FeatureSourcePaths | undefined> {
|
|
102
|
-
return this.adapter.getFeatureSourcePaths(featureKey);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
getFeaturePropertySourcePath(
|
|
106
|
-
featureKey: FeatureKey,
|
|
107
|
-
property: FeatureEnvironmentProperty,
|
|
108
|
-
environment?: string,
|
|
109
|
-
): Promise<string | undefined> {
|
|
110
|
-
return this.adapter.getFeaturePropertySourcePath(featureKey, property, environment);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
121
|
async getRequiredFeaturesChain(
|
|
114
122
|
featureKey: FeatureKey,
|
|
115
123
|
chain = new Set<FeatureKey>(),
|
|
@@ -244,6 +252,27 @@ export class Datasource {
|
|
|
244
252
|
return this.adapter.deleteEntity("schema", schemaKey);
|
|
245
253
|
}
|
|
246
254
|
|
|
255
|
+
// targets
|
|
256
|
+
listTargets() {
|
|
257
|
+
return this.adapter.listEntities("target");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
targetExists(targetKey: TargetKey) {
|
|
261
|
+
return this.adapter.entityExists("target", targetKey);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
readTarget(targetKey: TargetKey) {
|
|
265
|
+
return this.adapter.readEntity<Target>("target", targetKey);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
writeTarget(targetKey: TargetKey, target: Target) {
|
|
269
|
+
return this.adapter.writeEntity<Target>("target", targetKey, target);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
deleteTarget(targetKey: TargetKey) {
|
|
273
|
+
return this.adapter.deleteEntity("target", targetKey);
|
|
274
|
+
}
|
|
275
|
+
|
|
247
276
|
// tests
|
|
248
277
|
listTests() {
|
|
249
278
|
return this.adapter.listEntities("test");
|
|
@@ -4,100 +4,131 @@ import * as path from "path";
|
|
|
4
4
|
|
|
5
5
|
import { getProjectConfig } from "../config";
|
|
6
6
|
import { Datasource } from "./datasource";
|
|
7
|
+
import { getExistingStateFilePath } from "./filesystemAdapter";
|
|
7
8
|
|
|
8
9
|
function writeFile(filePath: string, content: string) {
|
|
9
10
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
10
11
|
fs.writeFileSync(filePath, content, "utf8");
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
function
|
|
14
|
+
function createProject(configBody: string) {
|
|
14
15
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "featurevisor-fs-adapter-"));
|
|
15
16
|
writeFile(path.join(root, "featurevisor.config.js"), configBody);
|
|
16
17
|
|
|
17
18
|
return root;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
describe("core: filesystemAdapter
|
|
21
|
-
it("
|
|
22
|
-
const root =
|
|
23
|
-
|
|
24
|
-
);
|
|
21
|
+
describe("core: filesystemAdapter", () => {
|
|
22
|
+
it("lists generated datafiles as sorted relative paths", async () => {
|
|
23
|
+
const root = createProject("module.exports = {};");
|
|
24
|
+
writeFile(path.join(root, "datafiles", "production", "featurevisor-all.json"), "{}");
|
|
25
|
+
writeFile(path.join(root, "datafiles", "staging", "nested", "featurevisor-web.json"), "abc");
|
|
26
|
+
writeFile(path.join(root, "datafiles", "staging", "featurevisor-mobile.txt"), "abcd");
|
|
27
|
+
writeFile(path.join(root, "datafiles", "REVISION"), "1");
|
|
28
|
+
writeFile(path.join(root, "datafiles", ".DS_Store"), "");
|
|
29
|
+
|
|
30
|
+
const datasource = new Datasource(getProjectConfig(root), root);
|
|
31
|
+
|
|
32
|
+
await expect(datasource.listDatafiles()).resolves.toMatchObject([
|
|
33
|
+
{ path: "production/featurevisor-all.json", size: 2, gzipSize: expect.any(Number) },
|
|
34
|
+
{ path: "staging/featurevisor-mobile.txt", size: 4, gzipSize: expect.any(Number) },
|
|
35
|
+
{ path: "staging/nested/featurevisor-web.json", size: 3, gzipSize: expect.any(Number) },
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("lists nested entity paths with dot namespace character by default", async () => {
|
|
40
|
+
const root = createProject("module.exports = {};");
|
|
25
41
|
|
|
26
42
|
writeFile(
|
|
27
|
-
path.join(root, "features", "checkout.yml"),
|
|
43
|
+
path.join(root, "features", "checkout", "page.yml"),
|
|
28
44
|
[
|
|
29
|
-
"
|
|
30
|
-
"description: Checkout feature",
|
|
45
|
+
"description: Checkout page",
|
|
31
46
|
"tags:",
|
|
32
47
|
" - all",
|
|
33
48
|
"bucketBy: userId",
|
|
49
|
+
"rules:",
|
|
50
|
+
" staging:",
|
|
51
|
+
" - key: everyone",
|
|
52
|
+
" segments: '*'",
|
|
53
|
+
" percentage: 100",
|
|
54
|
+
" production:",
|
|
55
|
+
" - key: everyone",
|
|
56
|
+
" segments: '*'",
|
|
57
|
+
" percentage: 100",
|
|
34
58
|
].join("\n"),
|
|
35
59
|
);
|
|
36
|
-
|
|
37
|
-
writeFile(
|
|
38
|
-
path.join(root, "environments", "staging", "checkout.yml"),
|
|
39
|
-
["rules:", " - key: everyone", " segments: '*'", " percentage: 100"].join("\n"),
|
|
40
|
-
);
|
|
41
|
-
|
|
42
60
|
writeFile(
|
|
43
|
-
path.join(root, "
|
|
61
|
+
path.join(root, "segments", "countries", "germany.yml"),
|
|
44
62
|
[
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
63
|
+
"description: Germany",
|
|
64
|
+
"conditions:",
|
|
65
|
+
" - attribute: country",
|
|
66
|
+
" operator: equals",
|
|
67
|
+
" value: de",
|
|
50
68
|
].join("\n"),
|
|
51
69
|
);
|
|
52
70
|
|
|
53
71
|
const config = getProjectConfig(root);
|
|
54
72
|
const datasource = new Datasource(config, root);
|
|
55
|
-
const feature = await datasource.readFeature("checkout");
|
|
56
|
-
const rulesByEnvironment = feature.rules as Record<string, any[]>;
|
|
57
|
-
const exposeByEnvironment = feature.expose as Record<string, unknown>;
|
|
58
73
|
|
|
59
|
-
expect(
|
|
60
|
-
expect(
|
|
61
|
-
|
|
74
|
+
await expect(datasource.listFeatures()).resolves.toEqual(["checkout.page"]);
|
|
75
|
+
await expect(datasource.listSegments()).resolves.toEqual(["countries.germany"]);
|
|
76
|
+
await datasource.writeTarget("apps.web", {
|
|
77
|
+
description: "Web app",
|
|
78
|
+
tags: ["all"],
|
|
79
|
+
context: { platform: "web" },
|
|
80
|
+
});
|
|
81
|
+
await expect(datasource.listTargets()).resolves.toEqual(["apps.web"]);
|
|
82
|
+
await expect(datasource.readTarget("apps.web")).resolves.toMatchObject({
|
|
83
|
+
description: "Web app",
|
|
84
|
+
tags: ["all"],
|
|
85
|
+
});
|
|
86
|
+
await datasource.deleteTarget("apps.web");
|
|
87
|
+
await expect(datasource.listTargets()).resolves.toEqual([]);
|
|
88
|
+
await expect(datasource.readFeature("checkout.page")).resolves.toMatchObject({
|
|
89
|
+
description: "Checkout page",
|
|
90
|
+
});
|
|
62
91
|
});
|
|
63
92
|
|
|
64
|
-
it("
|
|
65
|
-
const root =
|
|
66
|
-
"module.exports = { environments: ['staging', 'production'], splitByEnvironment: true };",
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
writeFile(
|
|
70
|
-
path.join(root, "features", "myFeature.yml"),
|
|
71
|
-
["key: myFeature", "description: My feature", "tags:", " - all", "bucketBy: userId"].join(
|
|
72
|
-
"\n",
|
|
73
|
-
),
|
|
74
|
-
);
|
|
93
|
+
it("keeps slash-separated keys when namespaceCharacter is slash", async () => {
|
|
94
|
+
const root = createProject('module.exports = { namespaceCharacter: "/" };');
|
|
75
95
|
|
|
76
96
|
writeFile(
|
|
77
|
-
path.join(root, "
|
|
78
|
-
[
|
|
97
|
+
path.join(root, "features", "checkout", "page.yml"),
|
|
98
|
+
[
|
|
99
|
+
"description: Checkout page",
|
|
100
|
+
"tags:",
|
|
101
|
+
" - all",
|
|
102
|
+
"bucketBy: userId",
|
|
103
|
+
"rules:",
|
|
104
|
+
" staging:",
|
|
105
|
+
" - key: everyone",
|
|
106
|
+
" segments: '*'",
|
|
107
|
+
" percentage: 100",
|
|
108
|
+
" production:",
|
|
109
|
+
" - key: everyone",
|
|
110
|
+
" segments: '*'",
|
|
111
|
+
" percentage: 100",
|
|
112
|
+
].join("\n"),
|
|
79
113
|
);
|
|
80
114
|
|
|
81
115
|
const config = getProjectConfig(root);
|
|
82
116
|
const datasource = new Datasource(config, root);
|
|
83
117
|
|
|
84
|
-
await expect(datasource.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
),
|
|
118
|
+
await expect(datasource.listFeatures()).resolves.toEqual(["checkout/page"]);
|
|
119
|
+
await expect(datasource.readFeature("checkout/page")).resolves.toMatchObject({
|
|
120
|
+
description: "Checkout page",
|
|
88
121
|
});
|
|
89
122
|
});
|
|
90
123
|
|
|
91
|
-
it("
|
|
92
|
-
const root =
|
|
93
|
-
"module.exports = { environments: ['staging'], splitByEnvironment: true };",
|
|
94
|
-
);
|
|
124
|
+
it("lists sets and reads entities from selected set", async () => {
|
|
125
|
+
const root = createProject("module.exports = { sets: true };");
|
|
95
126
|
|
|
96
127
|
writeFile(
|
|
97
|
-
path.join(root, "features", "
|
|
128
|
+
path.join(root, "sets", "storefront", "features", "banner.yml"),
|
|
98
129
|
[
|
|
99
|
-
"key:
|
|
100
|
-
"description:
|
|
130
|
+
"key: banner",
|
|
131
|
+
"description: Storefront banner",
|
|
101
132
|
"tags:",
|
|
102
133
|
" - all",
|
|
103
134
|
"bucketBy: userId",
|
|
@@ -106,48 +137,76 @@ describe("core: filesystemAdapter (splitByEnvironment)", () => {
|
|
|
106
137
|
" - key: everyone",
|
|
107
138
|
" segments: '*'",
|
|
108
139
|
" percentage: 100",
|
|
140
|
+
" production:",
|
|
141
|
+
" - key: everyone",
|
|
142
|
+
" segments: '*'",
|
|
143
|
+
" percentage: 100",
|
|
109
144
|
].join("\n"),
|
|
110
145
|
);
|
|
111
146
|
|
|
112
147
|
writeFile(
|
|
113
|
-
path.join(root, "
|
|
114
|
-
[
|
|
148
|
+
path.join(root, "sets", "admin", "features", "banner.yml"),
|
|
149
|
+
[
|
|
150
|
+
"key: banner",
|
|
151
|
+
"description: Admin banner",
|
|
152
|
+
"tags:",
|
|
153
|
+
" - all",
|
|
154
|
+
"bucketBy: userId",
|
|
155
|
+
"rules:",
|
|
156
|
+
" staging:",
|
|
157
|
+
" - key: everyone",
|
|
158
|
+
" segments: '*'",
|
|
159
|
+
" percentage: 0",
|
|
160
|
+
" production:",
|
|
161
|
+
" - key: everyone",
|
|
162
|
+
" segments: '*'",
|
|
163
|
+
" percentage: 0",
|
|
164
|
+
].join("\n"),
|
|
115
165
|
);
|
|
116
166
|
|
|
117
167
|
const config = getProjectConfig(root);
|
|
118
168
|
const datasource = new Datasource(config, root);
|
|
119
169
|
|
|
120
|
-
await expect(datasource.
|
|
121
|
-
|
|
170
|
+
await expect(datasource.listSets()).resolves.toEqual(["admin", "storefront"]);
|
|
171
|
+
|
|
172
|
+
const storefrontDatasource = datasource.forSet("storefront");
|
|
173
|
+
const feature = await storefrontDatasource.readFeature("banner");
|
|
174
|
+
|
|
175
|
+
expect(feature.description).toBe("Storefront banner");
|
|
176
|
+
expect(storefrontDatasource.getSet()).toBe("storefront");
|
|
177
|
+
expect(storefrontDatasource.getConfig().featuresDirectoryPath).toBe(
|
|
178
|
+
path.join(root, "sets", "storefront", "features"),
|
|
122
179
|
);
|
|
180
|
+
expect(storefrontDatasource.getConfig().targetsDirectoryPath).toBe(
|
|
181
|
+
path.join(root, "sets", "storefront", "targets"),
|
|
182
|
+
);
|
|
183
|
+
await storefrontDatasource.writeTarget("web", { description: "Storefront web" });
|
|
184
|
+
await expect(storefrontDatasource.listTargets()).resolves.toEqual(["web"]);
|
|
123
185
|
});
|
|
124
186
|
|
|
125
|
-
it("
|
|
126
|
-
const root =
|
|
127
|
-
|
|
128
|
-
);
|
|
187
|
+
it("writes compact state when prettyState is disabled", async () => {
|
|
188
|
+
const root = createProject("module.exports = { prettyState: false };");
|
|
189
|
+
const config = getProjectConfig(root);
|
|
190
|
+
const datasource = new Datasource(config, root);
|
|
191
|
+
const state = { features: {} };
|
|
129
192
|
|
|
130
|
-
|
|
131
|
-
path.join(root, "features", "foo.yml"),
|
|
132
|
-
["key: foo", "description: Foo", "tags:", " - all", "bucketBy: userId"].join("\n"),
|
|
133
|
-
);
|
|
193
|
+
await datasource.writeState(false, state);
|
|
134
194
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
[
|
|
138
|
-
"rules:",
|
|
139
|
-
" - key: everyone",
|
|
140
|
-
" segments: '*'",
|
|
141
|
-
" percentage: 100",
|
|
142
|
-
"extraKey: not allowed",
|
|
143
|
-
].join("\n"),
|
|
195
|
+
expect(fs.readFileSync(getExistingStateFilePath(config, false), "utf8")).toBe(
|
|
196
|
+
JSON.stringify(state),
|
|
144
197
|
);
|
|
198
|
+
});
|
|
145
199
|
|
|
200
|
+
it("writes formatted state when prettyState is enabled", async () => {
|
|
201
|
+
const root = createProject("module.exports = { prettyState: true };");
|
|
146
202
|
const config = getProjectConfig(root);
|
|
147
203
|
const datasource = new Datasource(config, root);
|
|
204
|
+
const state = { features: {} };
|
|
205
|
+
|
|
206
|
+
await datasource.writeState(false, state);
|
|
148
207
|
|
|
149
|
-
|
|
150
|
-
|
|
208
|
+
expect(fs.readFileSync(getExistingStateFilePath(config, false), "utf8")).toBe(
|
|
209
|
+
JSON.stringify(state, null, 2),
|
|
151
210
|
);
|
|
152
211
|
});
|
|
153
212
|
});
|