@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
|
@@ -5,16 +5,22 @@ import type {
|
|
|
5
5
|
NotGroupSegment,
|
|
6
6
|
Context,
|
|
7
7
|
} from "@featurevisor/types";
|
|
8
|
-
import
|
|
8
|
+
import { allSegmentsAreMatched } from "@featurevisor/sdk";
|
|
9
|
+
import type { Featurevisor } from "@featurevisor/sdk";
|
|
9
10
|
|
|
10
|
-
export function
|
|
11
|
-
|
|
11
|
+
export function applyContextToSegments(
|
|
12
|
+
featurevisor: Featurevisor,
|
|
12
13
|
segments: GroupSegment | GroupSegment[],
|
|
13
14
|
context: Context,
|
|
14
15
|
removeSegments: string[] = [],
|
|
15
16
|
): GroupSegment | GroupSegment[] {
|
|
16
|
-
const
|
|
17
|
-
|
|
17
|
+
const withContextApplied = applyContextToGroupSegments(
|
|
18
|
+
featurevisor,
|
|
19
|
+
segments,
|
|
20
|
+
context,
|
|
21
|
+
removeSegments,
|
|
22
|
+
);
|
|
23
|
+
const removed = removeRedundantGroupSegments(withContextApplied);
|
|
18
24
|
|
|
19
25
|
return removed;
|
|
20
26
|
}
|
|
@@ -80,9 +86,13 @@ export function removeRedundantGroupSegments(
|
|
|
80
86
|
) as GroupSegment[];
|
|
81
87
|
const filtered = processed.filter((gs) => gs !== "*");
|
|
82
88
|
|
|
83
|
-
//
|
|
89
|
+
// `not` negates the implicit AND of its children. Matched children are true
|
|
90
|
+
// and can be removed: not(true && X) becomes not(X). If every child was
|
|
91
|
+
// true, keep not("*") so it remains an always-false expression.
|
|
84
92
|
if (filtered.length === 0) {
|
|
85
|
-
return
|
|
93
|
+
return {
|
|
94
|
+
not: ["*"],
|
|
95
|
+
} as NotGroupSegment;
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
return {
|
|
@@ -94,8 +104,8 @@ export function removeRedundantGroupSegments(
|
|
|
94
104
|
return groupSegments;
|
|
95
105
|
}
|
|
96
106
|
|
|
97
|
-
export function
|
|
98
|
-
|
|
107
|
+
export function applyContextToGroupSegments(
|
|
108
|
+
featurevisor: Featurevisor,
|
|
99
109
|
groupSegments: GroupSegment | GroupSegment[],
|
|
100
110
|
context: Context,
|
|
101
111
|
removeSegments: string[] = [],
|
|
@@ -106,7 +116,7 @@ export function buildScopedGroupSegments(
|
|
|
106
116
|
|
|
107
117
|
if (Array.isArray(groupSegments)) {
|
|
108
118
|
return groupSegments.map((gs) =>
|
|
109
|
-
|
|
119
|
+
applyContextToGroupSegments(featurevisor, gs, context, removeSegments),
|
|
110
120
|
) as GroupSegment[];
|
|
111
121
|
}
|
|
112
122
|
|
|
@@ -115,7 +125,9 @@ export function buildScopedGroupSegments(
|
|
|
115
125
|
return "*";
|
|
116
126
|
}
|
|
117
127
|
|
|
118
|
-
const matched =
|
|
128
|
+
const matched = allSegmentsAreMatched(groupSegments, context, (segmentKey) =>
|
|
129
|
+
featurevisor.getSegment(segmentKey),
|
|
130
|
+
);
|
|
119
131
|
|
|
120
132
|
if (matched) {
|
|
121
133
|
return "*";
|
|
@@ -127,7 +139,7 @@ export function buildScopedGroupSegments(
|
|
|
127
139
|
if ("and" in groupSegments) {
|
|
128
140
|
return {
|
|
129
141
|
and: groupSegments.and.map((gs) =>
|
|
130
|
-
|
|
142
|
+
applyContextToGroupSegments(featurevisor, gs, context, removeSegments),
|
|
131
143
|
) as GroupSegment[],
|
|
132
144
|
} as AndGroupSegment;
|
|
133
145
|
}
|
|
@@ -135,7 +147,7 @@ export function buildScopedGroupSegments(
|
|
|
135
147
|
if ("or" in groupSegments) {
|
|
136
148
|
return {
|
|
137
149
|
or: groupSegments.or.map((gs) =>
|
|
138
|
-
|
|
150
|
+
applyContextToGroupSegments(featurevisor, gs, context, removeSegments),
|
|
139
151
|
) as GroupSegment[],
|
|
140
152
|
} as OrGroupSegment;
|
|
141
153
|
}
|
|
@@ -143,7 +155,7 @@ export function buildScopedGroupSegments(
|
|
|
143
155
|
if ("not" in groupSegments) {
|
|
144
156
|
return {
|
|
145
157
|
not: groupSegments.not.map((gs) =>
|
|
146
|
-
|
|
158
|
+
applyContextToGroupSegments(featurevisor, gs, context, removeSegments),
|
|
147
159
|
) as GroupSegment[],
|
|
148
160
|
} as NotGroupSegment;
|
|
149
161
|
}
|
|
@@ -2,11 +2,12 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as os from "os";
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
|
|
5
|
-
import type { DatafileContent, ExistingState, ParsedFeature } from "@featurevisor/types";
|
|
5
|
+
import type { DatafileContent, ExistingState, ParsedFeature, Segment } from "@featurevisor/types";
|
|
6
6
|
import { parsers } from "@featurevisor/parsers";
|
|
7
7
|
|
|
8
8
|
import type { ProjectConfig } from "../config";
|
|
9
9
|
import { buildDatafile } from "./buildDatafile";
|
|
10
|
+
import { buildTargetDatafile } from "./buildProject";
|
|
10
11
|
|
|
11
12
|
function createProjectConfig(root: string, stringify = true): ProjectConfig {
|
|
12
13
|
return {
|
|
@@ -15,17 +16,18 @@ function createProjectConfig(root: string, stringify = true): ProjectConfig {
|
|
|
15
16
|
attributesDirectoryPath: path.join(root, "attributes-missing"),
|
|
16
17
|
groupsDirectoryPath: path.join(root, "groups-missing"),
|
|
17
18
|
schemasDirectoryPath: path.join(root, "schemas-missing"),
|
|
19
|
+
targetsDirectoryPath: path.join(root, "targets-missing"),
|
|
18
20
|
testsDirectoryPath: path.join(root, "tests-missing"),
|
|
19
21
|
stateDirectoryPath: path.join(root, ".featurevisor"),
|
|
20
22
|
datafilesDirectoryPath: path.join(root, "datafiles"),
|
|
21
23
|
datafileNamePattern: "featurevisor-%s.json",
|
|
22
24
|
revisionFileName: "REVISION",
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
catalogDirectoryPath: path.join(root, "catalog"),
|
|
26
|
+
setsDirectoryPath: path.join(root, "sets"),
|
|
25
27
|
environments: ["staging", "production"],
|
|
26
|
-
|
|
28
|
+
sets: false,
|
|
29
|
+
namespaceCharacter: ".",
|
|
27
30
|
tags: ["all"],
|
|
28
|
-
scopes: [],
|
|
29
31
|
adapter: {},
|
|
30
32
|
plugins: [],
|
|
31
33
|
defaultBucketBy: "userId",
|
|
@@ -37,18 +39,33 @@ function createProjectConfig(root: string, stringify = true): ProjectConfig {
|
|
|
37
39
|
};
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
function
|
|
42
|
+
function isParsedFeature(
|
|
43
|
+
feature: ParsedFeature | Record<string, ParsedFeature>,
|
|
44
|
+
): feature is ParsedFeature {
|
|
45
|
+
return typeof (feature as ParsedFeature).description === "string";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function createMockDatasource(
|
|
49
|
+
feature: ParsedFeature | Record<string, ParsedFeature>,
|
|
50
|
+
segmentsByKey: Record<string, Segment> = {},
|
|
51
|
+
) {
|
|
52
|
+
let featuresByKey: Record<string, ParsedFeature>;
|
|
53
|
+
|
|
54
|
+
if (isParsedFeature(feature)) {
|
|
55
|
+
featuresByKey = { [feature.key]: feature };
|
|
56
|
+
} else {
|
|
57
|
+
featuresByKey = feature;
|
|
58
|
+
}
|
|
59
|
+
|
|
41
60
|
return {
|
|
42
61
|
listSchemas: async () => [],
|
|
43
62
|
readSchema: async () => {
|
|
44
63
|
throw new Error("readSchema should not be called");
|
|
45
64
|
},
|
|
46
|
-
listFeatures: async () =>
|
|
47
|
-
readFeature: async () =>
|
|
48
|
-
listSegments: async () =>
|
|
49
|
-
readSegment: async () =>
|
|
50
|
-
throw new Error("readSegment should not be called");
|
|
51
|
-
},
|
|
65
|
+
listFeatures: async () => Object.keys(featuresByKey),
|
|
66
|
+
readFeature: async (key: string) => featuresByKey[key],
|
|
67
|
+
listSegments: async () => Object.keys(segmentsByKey),
|
|
68
|
+
readSegment: async (key: string) => segmentsByKey[key],
|
|
52
69
|
listAttributes: async () => [],
|
|
53
70
|
readAttribute: async () => {
|
|
54
71
|
throw new Error("readAttribute should not be called");
|
|
@@ -162,6 +179,7 @@ describe("core: buildDatafile", function () {
|
|
|
162
179
|
beforeEach(function () {
|
|
163
180
|
root = fs.mkdtempSync(path.join(os.tmpdir(), "featurevisor-buildDatafile-"));
|
|
164
181
|
fs.mkdirSync(path.join(root, "features"), { recursive: true });
|
|
182
|
+
fs.mkdirSync(path.join(root, "segments-missing"), { recursive: true });
|
|
165
183
|
existingState = { features: {} };
|
|
166
184
|
});
|
|
167
185
|
|
|
@@ -177,13 +195,14 @@ describe("core: buildDatafile", function () {
|
|
|
177
195
|
config,
|
|
178
196
|
datasource,
|
|
179
197
|
{
|
|
180
|
-
schemaVersion: "2",
|
|
181
198
|
revision: "1",
|
|
182
199
|
environment: "staging",
|
|
183
200
|
},
|
|
184
201
|
existingState,
|
|
185
202
|
)) as DatafileContent;
|
|
186
203
|
|
|
204
|
+
expect(result.schemaVersion).toBe("2");
|
|
205
|
+
|
|
187
206
|
const trafficRule = result.features.withRuleOverrides.traffic.find((t) => t.key === "rule1");
|
|
188
207
|
|
|
189
208
|
expect(trafficRule).toBeDefined();
|
|
@@ -248,7 +267,6 @@ describe("core: buildDatafile", function () {
|
|
|
248
267
|
config,
|
|
249
268
|
datasource,
|
|
250
269
|
{
|
|
251
|
-
schemaVersion: "2",
|
|
252
270
|
revision: "1",
|
|
253
271
|
environment: "staging",
|
|
254
272
|
},
|
|
@@ -264,4 +282,164 @@ describe("core: buildDatafile", function () {
|
|
|
264
282
|
{ attribute: "country", operator: "equals", value: "de" },
|
|
265
283
|
]);
|
|
266
284
|
});
|
|
285
|
+
|
|
286
|
+
test("includes features with missing tags when no tag filter is provided", async function () {
|
|
287
|
+
const config = createProjectConfig(root, true);
|
|
288
|
+
const feature = createFeatureFixture();
|
|
289
|
+
delete feature.tags;
|
|
290
|
+
const datasource = createMockDatasource(feature);
|
|
291
|
+
|
|
292
|
+
const result = (await buildDatafile(
|
|
293
|
+
config,
|
|
294
|
+
datasource,
|
|
295
|
+
{
|
|
296
|
+
revision: "1",
|
|
297
|
+
environment: "staging",
|
|
298
|
+
},
|
|
299
|
+
existingState,
|
|
300
|
+
)) as DatafileContent;
|
|
301
|
+
|
|
302
|
+
expect(result.features.withRuleOverrides).toBeDefined();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
test("excludes features with missing tags when tag filter is provided", async function () {
|
|
306
|
+
const config = createProjectConfig(root, true);
|
|
307
|
+
const feature = createFeatureFixture();
|
|
308
|
+
delete feature.tags;
|
|
309
|
+
const datasource = createMockDatasource(feature);
|
|
310
|
+
|
|
311
|
+
const result = (await buildDatafile(
|
|
312
|
+
config,
|
|
313
|
+
datasource,
|
|
314
|
+
{
|
|
315
|
+
revision: "1",
|
|
316
|
+
environment: "staging",
|
|
317
|
+
tag: "all",
|
|
318
|
+
},
|
|
319
|
+
existingState,
|
|
320
|
+
)) as DatafileContent;
|
|
321
|
+
|
|
322
|
+
expect(result.features.withRuleOverrides).toBeUndefined();
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("filters target features by include/exclude patterns and combines them with tags using AND", async function () {
|
|
326
|
+
const config = createProjectConfig(root, true);
|
|
327
|
+
const createFeature = (key: string, tags: string[]) => ({
|
|
328
|
+
...createFeatureFixture(),
|
|
329
|
+
key,
|
|
330
|
+
description: key,
|
|
331
|
+
tags,
|
|
332
|
+
});
|
|
333
|
+
const datasource = createMockDatasource({
|
|
334
|
+
"checkout.web": createFeature("checkout.web", ["all", "web"]),
|
|
335
|
+
"checkout.mobile": createFeature("checkout.mobile", ["all", "mobile"]),
|
|
336
|
+
"admin.web": createFeature("admin.web", ["all", "web"]),
|
|
337
|
+
"admin.internal": createFeature("admin.internal", ["all", "web"]),
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const filtered = await buildTargetDatafile({
|
|
341
|
+
projectConfig: config,
|
|
342
|
+
datasource,
|
|
343
|
+
target: {
|
|
344
|
+
description: "Selected web features",
|
|
345
|
+
tag: "web",
|
|
346
|
+
includeFeatures: ["checkout*", "admin*"],
|
|
347
|
+
excludeFeatures: ["admin.internal"],
|
|
348
|
+
},
|
|
349
|
+
environment: "staging",
|
|
350
|
+
existingState,
|
|
351
|
+
revision: "1",
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
expect(Object.keys(filtered.features).sort()).toEqual(["admin.web", "checkout.web"]);
|
|
355
|
+
|
|
356
|
+
const all = await buildTargetDatafile({
|
|
357
|
+
projectConfig: config,
|
|
358
|
+
datasource,
|
|
359
|
+
target: {
|
|
360
|
+
description: "All except admin features",
|
|
361
|
+
includeFeatures: "*",
|
|
362
|
+
excludeFeatures: ["admin*"],
|
|
363
|
+
},
|
|
364
|
+
environment: "staging",
|
|
365
|
+
existingState,
|
|
366
|
+
revision: "1",
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
expect(Object.keys(all.features).sort()).toEqual(["checkout.mobile", "checkout.web"]);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
test("builds target datafiles without broadening NOT segment rules", async function () {
|
|
373
|
+
const config = createProjectConfig(root, false);
|
|
374
|
+
const feature: ParsedFeature = {
|
|
375
|
+
key: "targeted",
|
|
376
|
+
description: "Targeted feature",
|
|
377
|
+
tags: ["all"],
|
|
378
|
+
bucketBy: "userId",
|
|
379
|
+
rules: {
|
|
380
|
+
staging: [
|
|
381
|
+
{
|
|
382
|
+
key: "not-web",
|
|
383
|
+
segments: {
|
|
384
|
+
not: ["web"],
|
|
385
|
+
},
|
|
386
|
+
percentage: 100,
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
key: "not-web-and-chrome",
|
|
390
|
+
segments: {
|
|
391
|
+
not: ["web", "chrome"],
|
|
392
|
+
},
|
|
393
|
+
percentage: 100,
|
|
394
|
+
},
|
|
395
|
+
],
|
|
396
|
+
},
|
|
397
|
+
} as ParsedFeature;
|
|
398
|
+
const datasource = createMockDatasource(feature, {
|
|
399
|
+
web: {
|
|
400
|
+
conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
|
|
401
|
+
},
|
|
402
|
+
chrome: {
|
|
403
|
+
conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const webDatafile = await buildTargetDatafile({
|
|
408
|
+
projectConfig: config,
|
|
409
|
+
datasource,
|
|
410
|
+
target: {
|
|
411
|
+
description: "Web target",
|
|
412
|
+
context: { platform: "web" },
|
|
413
|
+
},
|
|
414
|
+
environment: "staging",
|
|
415
|
+
existingState,
|
|
416
|
+
revision: "1",
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
expect(webDatafile.features.targeted.traffic[0].segments).toEqual({
|
|
420
|
+
not: ["*"],
|
|
421
|
+
});
|
|
422
|
+
expect(webDatafile.features.targeted.traffic[1].segments).toEqual({
|
|
423
|
+
not: ["chrome"],
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const webChromeDatafile = await buildTargetDatafile({
|
|
427
|
+
projectConfig: config,
|
|
428
|
+
datasource,
|
|
429
|
+
target: {
|
|
430
|
+
description: "Web Chrome target",
|
|
431
|
+
context: { platform: "web", browser: "chrome" },
|
|
432
|
+
},
|
|
433
|
+
environment: "staging",
|
|
434
|
+
existingState,
|
|
435
|
+
revision: "1",
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
expect(webChromeDatafile.features.targeted.traffic[0].segments).toEqual({
|
|
439
|
+
not: ["*"],
|
|
440
|
+
});
|
|
441
|
+
expect(webChromeDatafile.features.targeted.traffic[1].segments).toEqual({
|
|
442
|
+
not: ["*"],
|
|
443
|
+
});
|
|
444
|
+
});
|
|
267
445
|
});
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
Segment,
|
|
6
6
|
Feature,
|
|
7
7
|
DatafileContent,
|
|
8
|
-
DatafileContentV1,
|
|
9
8
|
Variation,
|
|
10
9
|
VariableOverride,
|
|
11
10
|
Traffic,
|
|
@@ -22,14 +21,13 @@ import {
|
|
|
22
21
|
Force,
|
|
23
22
|
} from "@featurevisor/types";
|
|
24
23
|
|
|
25
|
-
import { ProjectConfig
|
|
24
|
+
import { ProjectConfig } from "../config";
|
|
26
25
|
import { Datasource } from "../datasource";
|
|
27
26
|
import { extractAttributeKeysFromConditions, extractSegmentKeysFromGroupSegments } from "../utils";
|
|
28
27
|
import { generateHashForDatafile, generateHashForFeature, getSegmentHashes } from "./hashes";
|
|
29
28
|
|
|
30
29
|
import { getTraffic } from "./traffic";
|
|
31
30
|
import { getFeatureRanges } from "./getFeatureRanges";
|
|
32
|
-
import { convertToV1 } from "./convertToV1";
|
|
33
31
|
import {
|
|
34
32
|
resolveMutationsForMultipleVariables,
|
|
35
33
|
resolveMutationsForSingleVariable,
|
|
@@ -37,19 +35,19 @@ import {
|
|
|
37
35
|
|
|
38
36
|
export interface CustomDatafileOptions {
|
|
39
37
|
featureKey?: string;
|
|
40
|
-
environment: string;
|
|
38
|
+
environment: string | false;
|
|
41
39
|
projectConfig: ProjectConfig;
|
|
42
40
|
datasource: Datasource;
|
|
43
41
|
revision?: string;
|
|
44
|
-
schemaVersion?: string;
|
|
45
42
|
inflate?: number;
|
|
46
43
|
tag?: string;
|
|
47
44
|
tags?: BuildTags;
|
|
45
|
+
includeFeatures?: "*" | FeatureKey[];
|
|
46
|
+
excludeFeatures?: "*" | FeatureKey[];
|
|
47
|
+
featurevisorVersion?: string;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
export async function getCustomDatafile(
|
|
51
|
-
options: CustomDatafileOptions,
|
|
52
|
-
): Promise<DatafileContent | DatafileContentV1> {
|
|
50
|
+
export async function getCustomDatafile(options: CustomDatafileOptions): Promise<DatafileContent> {
|
|
53
51
|
let featuresToInclude;
|
|
54
52
|
|
|
55
53
|
if (options.featureKey) {
|
|
@@ -62,13 +60,15 @@ export async function getCustomDatafile(
|
|
|
62
60
|
options.projectConfig,
|
|
63
61
|
options.datasource,
|
|
64
62
|
{
|
|
65
|
-
schemaVersion: options.schemaVersion || SCHEMA_VERSION,
|
|
66
63
|
revision: options.revision || "tester",
|
|
67
64
|
environment: options.environment,
|
|
68
65
|
features: featuresToInclude,
|
|
69
66
|
inflate: options.inflate,
|
|
70
67
|
tag: options.tag,
|
|
71
68
|
tags: options.tags,
|
|
69
|
+
includeFeatures: options.includeFeatures,
|
|
70
|
+
excludeFeatures: options.excludeFeatures,
|
|
71
|
+
featurevisorVersion: options.featurevisorVersion,
|
|
72
72
|
},
|
|
73
73
|
existingState,
|
|
74
74
|
);
|
|
@@ -86,14 +86,48 @@ export interface BuildAndTags {
|
|
|
86
86
|
|
|
87
87
|
export type BuildTags = BuildOrTags | BuildAndTags | string[];
|
|
88
88
|
|
|
89
|
+
function matchesBuildTags(candidateTags: string[], buildTags?: BuildTags): boolean {
|
|
90
|
+
if (!buildTags) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (Array.isArray(buildTags)) {
|
|
95
|
+
return buildTags.some((searchTag) => candidateTags.indexOf(searchTag) !== -1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if ("or" in buildTags) {
|
|
99
|
+
return buildTags.or.some((searchTag) => candidateTags.indexOf(searchTag) !== -1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if ("and" in buildTags) {
|
|
103
|
+
return buildTags.and.every((searchTag) => candidateTags.indexOf(searchTag) !== -1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function matchesFeaturePatterns(featureKey: FeatureKey, patterns?: "*" | FeatureKey[]): boolean {
|
|
110
|
+
if (!patterns) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const normalizedPatterns = patterns === "*" ? [patterns] : patterns;
|
|
115
|
+
|
|
116
|
+
return normalizedPatterns.some((pattern) => {
|
|
117
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
118
|
+
return new RegExp(`^${escaped}$`).test(featureKey);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
89
122
|
export interface BuildOptions {
|
|
90
|
-
schemaVersion: string;
|
|
91
123
|
revision: string;
|
|
92
124
|
featurevisorVersion?: string;
|
|
93
125
|
revisionFromHash?: boolean;
|
|
94
126
|
environment: string | false;
|
|
95
127
|
tag?: string;
|
|
96
128
|
tags?: BuildTags;
|
|
129
|
+
includeFeatures?: "*" | FeatureKey[];
|
|
130
|
+
excludeFeatures?: "*" | FeatureKey[];
|
|
97
131
|
features?: FeatureKey[];
|
|
98
132
|
inflate?: number;
|
|
99
133
|
}
|
|
@@ -103,7 +137,7 @@ export async function buildDatafile(
|
|
|
103
137
|
datasource: Datasource,
|
|
104
138
|
options: BuildOptions,
|
|
105
139
|
existingState: ExistingState,
|
|
106
|
-
): Promise<DatafileContent
|
|
140
|
+
): Promise<DatafileContent> {
|
|
107
141
|
const segmentKeysUsedByTag = new Set<SegmentKey>();
|
|
108
142
|
const attributeKeysUsedByTag = new Set<AttributeKey>();
|
|
109
143
|
const { featureRanges, featureIsInGroup } = await getFeatureRanges(projectConfig, datasource);
|
|
@@ -133,40 +167,25 @@ export async function buildDatafile(
|
|
|
133
167
|
continue;
|
|
134
168
|
}
|
|
135
169
|
|
|
136
|
-
|
|
170
|
+
const featureTags = parsedFeature.tags || [];
|
|
171
|
+
|
|
172
|
+
if (options.tag && featureTags.indexOf(options.tag) === -1) {
|
|
137
173
|
continue;
|
|
138
174
|
}
|
|
139
175
|
|
|
140
|
-
if (options.tags) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
options.tags.some((searchTag) => parsedFeature.tags.indexOf(searchTag) !== -1) === false
|
|
145
|
-
) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
// OR tags
|
|
150
|
-
const orTags = options.tags as BuildOrTags;
|
|
151
|
-
if (orTags.or) {
|
|
152
|
-
if (
|
|
153
|
-
orTags.or.some((searchTag) => parsedFeature.tags.indexOf(searchTag) !== -1) === false
|
|
154
|
-
) {
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
176
|
+
if (options.tags && matchesBuildTags(featureTags, options.tags) === false) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
158
179
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
}
|
|
180
|
+
if (
|
|
181
|
+
options.includeFeatures &&
|
|
182
|
+
matchesFeaturePatterns(featureKey, options.includeFeatures) === false
|
|
183
|
+
) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (options.excludeFeatures && matchesFeaturePatterns(featureKey, options.excludeFeatures)) {
|
|
188
|
+
continue;
|
|
170
189
|
}
|
|
171
190
|
|
|
172
191
|
if (options.features && options.features.indexOf(featureKey) === -1) {
|
|
@@ -197,6 +216,10 @@ export async function buildDatafile(
|
|
|
197
216
|
if (options.tag && exposeTags.indexOf(options.tag) === -1) {
|
|
198
217
|
continue;
|
|
199
218
|
}
|
|
219
|
+
|
|
220
|
+
if (options.tags && matchesBuildTags(exposeTags, options.tags) === false) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
200
223
|
}
|
|
201
224
|
|
|
202
225
|
for (const parsedRule of rules) {
|
|
@@ -569,19 +592,7 @@ export async function buildDatafile(
|
|
|
569
592
|
}
|
|
570
593
|
}
|
|
571
594
|
|
|
572
|
-
|
|
573
|
-
if (options.schemaVersion === "1") {
|
|
574
|
-
return convertToV1({
|
|
575
|
-
revision: options.revision,
|
|
576
|
-
projectConfig,
|
|
577
|
-
attributes,
|
|
578
|
-
features,
|
|
579
|
-
segments,
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// schema v2
|
|
584
|
-
const datafileContentV2: DatafileContent = {
|
|
595
|
+
const datafileContent: DatafileContent = {
|
|
585
596
|
schemaVersion: "2",
|
|
586
597
|
featurevisorVersion: options.featurevisorVersion,
|
|
587
598
|
revision: options.revision,
|
|
@@ -589,8 +600,7 @@ export async function buildDatafile(
|
|
|
589
600
|
features: {},
|
|
590
601
|
};
|
|
591
602
|
|
|
592
|
-
|
|
593
|
-
// key check needed for supporting v1 datafile generation
|
|
603
|
+
datafileContent.segments = segments.reduce((acc, segment) => {
|
|
594
604
|
if (segment.key) {
|
|
595
605
|
acc[segment.key] = segment;
|
|
596
606
|
delete acc[segment.key].key; // remove key from segment, as it is not needed in v2 datafile
|
|
@@ -599,7 +609,7 @@ export async function buildDatafile(
|
|
|
599
609
|
return acc;
|
|
600
610
|
}, {});
|
|
601
611
|
|
|
602
|
-
|
|
612
|
+
datafileContent.features = features.reduce((acc, feature) => {
|
|
603
613
|
if (!feature.key) {
|
|
604
614
|
return acc;
|
|
605
615
|
}
|
|
@@ -625,11 +635,11 @@ export async function buildDatafile(
|
|
|
625
635
|
}, {});
|
|
626
636
|
|
|
627
637
|
// add feature hashes for change detection
|
|
628
|
-
const segmentHashes = getSegmentHashes(
|
|
629
|
-
Object.keys(
|
|
630
|
-
const hash = generateHashForFeature(featureKey,
|
|
638
|
+
const segmentHashes = getSegmentHashes(datafileContent.segments);
|
|
639
|
+
Object.keys(datafileContent.features).forEach((featureKey) => {
|
|
640
|
+
const hash = generateHashForFeature(featureKey, datafileContent.features, segmentHashes);
|
|
631
641
|
|
|
632
|
-
|
|
642
|
+
datafileContent.features[featureKey].hash = hash;
|
|
633
643
|
|
|
634
644
|
// check needed to support --inflate option
|
|
635
645
|
if (existingState.features[featureKey]) {
|
|
@@ -638,9 +648,9 @@ export async function buildDatafile(
|
|
|
638
648
|
});
|
|
639
649
|
|
|
640
650
|
if (options.revisionFromHash) {
|
|
641
|
-
const datafileHash = generateHashForDatafile(
|
|
642
|
-
|
|
651
|
+
const datafileHash = generateHashForDatafile(datafileContent);
|
|
652
|
+
datafileContent.revision = `${datafileHash}`;
|
|
643
653
|
}
|
|
644
654
|
|
|
645
|
-
return
|
|
655
|
+
return datafileContent;
|
|
646
656
|
}
|