@@ -3,43 +3,52 @@ import { validate as uuidValidate, version as uuidVersion } from 'uuid';
33
44import type { FeatureFlagScopeValue } from '../remote-feature-flag-controller-types' ;
55
6+ /**
7+ * Converts a UUID string to a BigInt by removing dashes and converting to hexadecimal.
8+ * @param uuid - The UUID string to convert
9+ * @returns The UUID as a BigInt value
10+ */
11+ function uuidStringToBigInt ( uuid : string ) : bigint {
12+ return BigInt ( `0x${ uuid . replace ( / - / gu, '' ) } ` ) ;
13+ }
14+
15+ const MIN_UUID_V4 = '00000000-0000-4000-8000-000000000000' ;
16+ const MAX_UUID_V4 = 'ffffffff-ffff-4fff-bfff-ffffffffffff' ;
17+ const MIN_UUID_V4_BIGINT = uuidStringToBigInt ( MIN_UUID_V4 ) ;
18+ const MAX_UUID_V4_BIGINT = uuidStringToBigInt ( MAX_UUID_V4 ) ;
19+ const UUID_V4_VALUE_RANGE_BIGINT =
20+ MAX_UUID_V4_BIGINT - MIN_UUID_V4_BIGINT ;
21+
622/**
723 * Generates a deterministic random number between 0 and 1 based on a metaMetricsId.
824 * This is useful for A/B testing and feature flag rollouts where we want
925 * consistent group assignment for the same user.
10- *
11- * Supports two metaMetricsId formats:
12- * - UUIDv4 format (new mobile implementation)
13- * - Hex format with 0x prefix (extension or old mobile implementation)
14- *
15- * For UUIDv4 format, the following normalizations are applied:
16- * - Removes all dashes from the UUID
17- * - Remove version (4) bits and replace with 'f'
18- * - Converts the remaining hex string to a BigInt for calculation
19- *
20- * For hex format:
21- * - Expects a hex string with '0x' prefix (e.g., '0x1234abcd')
22- * - Removes the '0x' prefix before conversion
23- * - Converts the remaining hex string to a BigInt for calculation
24- *
25- * @param metaMetricsId - The unique identifier used to generate the deterministic random number, can be a UUIDv4 or hex string
26- * @returns A number between 0 and 1 that is deterministic for the given metaMetricsId
26+ * @param metaMetricsId - The unique identifier used to generate the deterministic random number. Must be either:
27+ * - A UUIDv4 string (e.g., '123e4567-e89b-12d3-a456-426614174000')
28+ * - A hex string with '0x' prefix (e.g., '0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420')
29+ * @returns A number between 0 and 1, deterministically generated from the input ID.
30+ * The same input will always produce the same output.
2731 */
2832export function generateDeterministicRandomNumber (
2933 metaMetricsId : string ,
3034) : number {
31- let cleanId : string ;
35+ let idValue : bigint ;
36+ let maxValue : bigint ;
3237 // uuidv4 format
3338 if ( uuidValidate ( metaMetricsId ) && uuidVersion ( metaMetricsId ) === 4 ) {
34- cleanId = metaMetricsId . replace ( / - / gu, '' ) . replace ( / ^ ( .{ 12 } ) 4 / u, '$1f' ) ;
39+ // Normalize the UUIDv4 range to start from 0 by subtracting MIN_UUID_V4_BIGINT.
40+ // This ensures uniform distribution across the entire range, since UUIDv4
41+ // has restricted bits for version (4) and variant (8-b) that would otherwise skew the distribution
42+ idValue = uuidStringToBigInt ( metaMetricsId ) - MIN_UUID_V4_BIGINT ;
43+ maxValue = UUID_V4_VALUE_RANGE_BIGINT ;
3544 } else {
3645 // hex format with 0x prefix
37- cleanId = metaMetricsId . slice ( 2 ) ;
46+ const cleanId = metaMetricsId . slice ( 2 ) ;
47+ idValue = BigInt ( `0x${ cleanId } ` ) ;
48+ maxValue = BigInt ( `0x${ 'f' . repeat ( cleanId . length ) } ` ) ;
3849 }
39- const value = BigInt ( `0x${ cleanId } ` ) ;
40- const maxValue = BigInt ( `0x${ 'f' . repeat ( cleanId . length ) } ` ) ;
4150 // Use BigInt division first, then convert to number to maintain precision
42- return Number ( ( value * BigInt ( 1_000_000 ) ) / maxValue ) / 1_000_000 ;
51+ return Number ( ( idValue * BigInt ( 1_000_000 ) ) / maxValue ) / 1_000_000 ;
4352}
4453
4554/**
0 commit comments