|
1 | 1 | import { EvaluationContext, FeatureContext } from './evaluation/models.js'; |
2 | 2 | import { getIdentitySegments } from './segments/evaluators.js'; |
3 | 3 | import { EvaluationResult, EvaluationResultFlags } from './evaluation/models.js'; |
4 | | -import { evaluateFeatureValue } from './features/util.js'; |
5 | 4 | import { TARGETING_REASONS } from './features/types.js'; |
| 5 | +import { getHashedPercentageForObjIds } from './utils/hashing/index.js'; |
6 | 6 | export { EnvironmentModel } from './environments/models.js'; |
7 | 7 | export { IdentityModel } from './identities/models.js'; |
8 | 8 | export { TraitModel } from './identities/traits/models.js'; |
@@ -124,6 +124,39 @@ export function evaluateFeatures( |
124 | 124 | return flags; |
125 | 125 | } |
126 | 126 |
|
| 127 | +function evaluateFeatureValue(feature: FeatureContext, identityKey?: string): any { |
| 128 | + if (!!feature.variants && feature.variants.length > 0 && !!identityKey) { |
| 129 | + return getMultivariateFeatureValue(feature, identityKey); |
| 130 | + } |
| 131 | + |
| 132 | + return feature.value; |
| 133 | +} |
| 134 | + |
| 135 | +/** |
| 136 | + * Evaluates a multivariate feature flag to determine which variant value to return for a given identity. |
| 137 | + * |
| 138 | + * Uses deterministic hashing to ensure the same identity always receives the same variant, |
| 139 | + * while distributing variants according to their configured weight percentages. |
| 140 | + * |
| 141 | + * @param feature - The feature context containing variants and their weights |
| 142 | + * @param identityKey - The identity key used for deterministic variant selection |
| 143 | + * @returns The variant value if the identity falls within a variant's range, otherwise the default feature value |
| 144 | + */ |
| 145 | +function getMultivariateFeatureValue(feature: FeatureContext, identityKey?: string): any { |
| 146 | + const percentageValue = getHashedPercentageForObjIds([feature.key, identityKey]); |
| 147 | + |
| 148 | + let startPercentage = 0; |
| 149 | + for (const variant of feature?.variants || []) { |
| 150 | + const limit = startPercentage + variant.weight; |
| 151 | + |
| 152 | + if (startPercentage <= percentageValue && percentageValue < limit) { |
| 153 | + return variant.value; |
| 154 | + } |
| 155 | + startPercentage = limit; |
| 156 | + } |
| 157 | + return feature.value; |
| 158 | +} |
| 159 | + |
127 | 160 | export function shouldApplyOverride( |
128 | 161 | override: any, |
129 | 162 | existingOverrides: Record<string, SegmentOverride> |
|
0 commit comments