@metamask-previews/remote-feature-flag-controller 1.2.0-preview-2ebcbae7 → 1.2.0-preview-ae144301

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.
@@ -1,21 +1,62 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isFeatureFlagWithScopeValue = exports.generateDeterministicRandomNumber = void 0;
4
- /* eslint-disable no-bitwise */
4
+ const uuid_1 = require("uuid");
5
+ /**
6
+ * Converts a UUID string to a BigInt by removing dashes and converting to hexadecimal.
7
+ * @param uuid - The UUID string to convert
8
+ * @returns The UUID as a BigInt value
9
+ */
10
+ function uuidStringToBigInt(uuid) {
11
+ return BigInt(`0x${uuid.replace(/-/gu, '')}`);
12
+ }
13
+ const MIN_UUID_V4 = '00000000-0000-4000-8000-000000000000';
14
+ const MAX_UUID_V4 = 'ffffffff-ffff-4fff-bfff-ffffffffffff';
15
+ const MIN_UUID_V4_BIGINT = uuidStringToBigInt(MIN_UUID_V4);
16
+ const MAX_UUID_V4_BIGINT = uuidStringToBigInt(MAX_UUID_V4);
17
+ const UUID_V4_VALUE_RANGE_BIGINT = MAX_UUID_V4_BIGINT - MIN_UUID_V4_BIGINT;
5
18
  /**
6
19
  * Generates a deterministic random number between 0 and 1 based on a metaMetricsId.
7
20
  * This is useful for A/B testing and feature flag rollouts where we want
8
21
  * consistent group assignment for the same user.
9
- *
10
- * @param metaMetricsId - The unique identifier used to generate the deterministic random number
11
- * @returns A number between 0 and 1 that is deterministic for the given metaMetricsId
22
+ * @param metaMetricsId - The unique identifier used to generate the deterministic random number. Must be either:
23
+ * - A UUIDv4 string (e.g., '123e4567-e89b-12d3-a456-426614174000'
24
+ * - A hex string with '0x' prefix (e.g., '0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420')
25
+ * @returns A number between 0 and 1, deterministically generated from the input ID.
26
+ * The same input will always produce the same output.
12
27
  */
13
28
  function generateDeterministicRandomNumber(metaMetricsId) {
14
- const hash = [...metaMetricsId].reduce((acc, char) => {
15
- const chr = char.charCodeAt(0);
16
- return ((acc << 5) - acc + chr) | 0;
17
- }, 0);
18
- return (hash >>> 0) / 0xffffffff;
29
+ if (!metaMetricsId) {
30
+ throw new Error('MetaMetrics ID cannot be empty');
31
+ }
32
+ let idValue;
33
+ let maxValue;
34
+ // uuidv4 format
35
+ if ((0, uuid_1.validate)(metaMetricsId)) {
36
+ if ((0, uuid_1.version)(metaMetricsId) !== 4) {
37
+ throw new Error(`Invalid UUID version. Expected v4, got v${(0, uuid_1.version)(metaMetricsId)}`);
38
+ }
39
+ idValue = uuidStringToBigInt(metaMetricsId) - MIN_UUID_V4_BIGINT;
40
+ maxValue = UUID_V4_VALUE_RANGE_BIGINT;
41
+ }
42
+ else {
43
+ // hex format with 0x prefix
44
+ if (!metaMetricsId.startsWith('0x')) {
45
+ throw new Error('Hex ID must start with 0x prefix');
46
+ }
47
+ const cleanId = metaMetricsId.slice(2);
48
+ const EXPECTED_HEX_LENGTH = 64; // 32 bytes = 64 hex characters
49
+ if (cleanId.length !== EXPECTED_HEX_LENGTH) {
50
+ throw new Error(`Invalid hex ID length. Expected ${EXPECTED_HEX_LENGTH} characters, got ${cleanId.length}`);
51
+ }
52
+ if (!/^[0-9a-f]+$/iu.test(cleanId)) {
53
+ throw new Error('Hex ID contains invalid characters');
54
+ }
55
+ idValue = BigInt(`0x${cleanId}`);
56
+ maxValue = BigInt(`0x${'f'.repeat(cleanId.length)}`);
57
+ }
58
+ // Use BigInt division first, then convert to number to maintain precision
59
+ return Number((idValue * BigInt(1000000)) / maxValue) / 1000000;
19
60
  }
20
61
  exports.generateDeterministicRandomNumber = generateDeterministicRandomNumber;
21
62
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"user-segmentation-utils.cjs","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":";;;AAIA,+BAA+B;AAC/B;;;;;;;GAOG;AACH,SAAgB,iCAAiC,CAC/C,aAAqB;IAErB,MAAM,IAAI,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC/B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEN,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,GAAG,UAAU,CAAC;AACnC,CAAC;AATD,8EASC;AAED;;;;;;GAMG;AACI,MAAM,2BAA2B,GAAG,CACzC,WAAiB,EACqB,EAAE;IACxC,OAAO,CACL,OAAO,WAAW,KAAK,QAAQ;QAC/B,WAAW,KAAK,IAAI;QACpB,OAAO,IAAI,WAAW,CACvB,CAAC;AACJ,CAAC,CAAC;AARW,QAAA,2BAA2B,+BAQtC","sourcesContent":["import type { Json } from '@metamask/utils';\n\nimport type { FeatureFlagScopeValue } from '../remote-feature-flag-controller-types';\n\n/* eslint-disable no-bitwise */\n/**\n * Generates a deterministic random number between 0 and 1 based on a metaMetricsId.\n * This is useful for A/B testing and feature flag rollouts where we want\n * consistent group assignment for the same user.\n *\n * @param metaMetricsId - The unique identifier used to generate the deterministic random number\n * @returns A number between 0 and 1 that is deterministic for the given metaMetricsId\n */\nexport function generateDeterministicRandomNumber(\n metaMetricsId: string,\n): number {\n const hash = [...metaMetricsId].reduce((acc, char) => {\n const chr = char.charCodeAt(0);\n return ((acc << 5) - acc + chr) | 0;\n }, 0);\n\n return (hash >>> 0) / 0xffffffff;\n}\n\n/**\n * Type guard to check if a value is a feature flag with scope.\n * Used to validate feature flag objects that contain scope-based configurations.\n *\n * @param featureFlag - The value to check if it's a feature flag with scope\n * @returns True if the value is a feature flag with scope, false otherwise\n */\nexport const isFeatureFlagWithScopeValue = (\n featureFlag: Json,\n): featureFlag is FeatureFlagScopeValue => {\n return (\n typeof featureFlag === 'object' &&\n featureFlag !== null &&\n 'scope' in featureFlag\n );\n};\n"]}
1
+ {"version":3,"file":"user-segmentation-utils.cjs","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":";;;AACA,+BAAwE;AAIxE;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,IAAY;IACtC,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,WAAW,GAAG,sCAAsC,CAAC;AAC3D,MAAM,WAAW,GAAG,sCAAsC,CAAC;AAC3D,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;AAC3D,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;AAC3D,MAAM,0BAA0B,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAE3E;;;;;;;;;GASG;AACH,SAAgB,iCAAiC,CAC/C,aAAqB;IAErB,IAAI,CAAC,aAAa,EAAE;QAClB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;KACnD;IAED,IAAI,OAAe,CAAC;IACpB,IAAI,QAAgB,CAAC;IAErB,gBAAgB;IAChB,IAAI,IAAA,eAAY,EAAC,aAAa,CAAC,EAAE;QAC/B,IAAI,IAAA,cAAW,EAAC,aAAa,CAAC,KAAK,CAAC,EAAE;YACpC,MAAM,IAAI,KAAK,CACb,2CAA2C,IAAA,cAAW,EAAC,aAAa,CAAC,EAAE,CACxE,CAAC;SACH;QACD,OAAO,GAAG,kBAAkB,CAAC,aAAa,CAAC,GAAG,kBAAkB,CAAC;QACjE,QAAQ,GAAG,0BAA0B,CAAC;KACvC;SAAM;QACL,4BAA4B;QAC5B,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;YACnC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;SACrD;QAED,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,mBAAmB,GAAG,EAAE,CAAC,CAAC,+BAA+B;QAE/D,IAAI,OAAO,CAAC,MAAM,KAAK,mBAAmB,EAAE;YAC1C,MAAM,IAAI,KAAK,CACb,mCAAmC,mBAAmB,oBAAoB,OAAO,CAAC,MAAM,EAAE,CAC3F,CAAC;SACH;QAED,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YAClC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;SACvD;QAED,OAAO,GAAG,MAAM,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;QACjC,QAAQ,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;KACtD;IAED,0EAA0E;IAC1E,OAAO,MAAM,CAAC,CAAC,OAAO,GAAG,MAAM,CAAC,OAAS,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,OAAS,CAAC;AACtE,CAAC;AA5CD,8EA4CC;AAED;;;;;;GAMG;AACI,MAAM,2BAA2B,GAAG,CACzC,WAAiB,EACqB,EAAE;IACxC,OAAO,CACL,OAAO,WAAW,KAAK,QAAQ;QAC/B,WAAW,KAAK,IAAI;QACpB,OAAO,IAAI,WAAW,CACvB,CAAC;AACJ,CAAC,CAAC;AARW,QAAA,2BAA2B,+BAQtC","sourcesContent":["import type { Json } from '@metamask/utils';\nimport { validate as uuidValidate, version as uuidVersion } from 'uuid';\n\nimport type { FeatureFlagScopeValue } from '../remote-feature-flag-controller-types';\n\n/**\n * Converts a UUID string to a BigInt by removing dashes and converting to hexadecimal.\n * @param uuid - The UUID string to convert\n * @returns The UUID as a BigInt value\n */\nfunction uuidStringToBigInt(uuid: string): bigint {\n return BigInt(`0x${uuid.replace(/-/gu, '')}`);\n}\n\nconst MIN_UUID_V4 = '00000000-0000-4000-8000-000000000000';\nconst MAX_UUID_V4 = 'ffffffff-ffff-4fff-bfff-ffffffffffff';\nconst MIN_UUID_V4_BIGINT = uuidStringToBigInt(MIN_UUID_V4);\nconst MAX_UUID_V4_BIGINT = uuidStringToBigInt(MAX_UUID_V4);\nconst UUID_V4_VALUE_RANGE_BIGINT = MAX_UUID_V4_BIGINT - MIN_UUID_V4_BIGINT;\n\n/**\n * Generates a deterministic random number between 0 and 1 based on a metaMetricsId.\n * This is useful for A/B testing and feature flag rollouts where we want\n * consistent group assignment for the same user.\n * @param metaMetricsId - The unique identifier used to generate the deterministic random number. Must be either:\n * - A UUIDv4 string (e.g., '123e4567-e89b-12d3-a456-426614174000'\n * - A hex string with '0x' prefix (e.g., '0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420')\n * @returns A number between 0 and 1, deterministically generated from the input ID.\n * The same input will always produce the same output.\n */\nexport function generateDeterministicRandomNumber(\n metaMetricsId: string,\n): number {\n if (!metaMetricsId) {\n throw new Error('MetaMetrics ID cannot be empty');\n }\n\n let idValue: bigint;\n let maxValue: bigint;\n\n // uuidv4 format\n if (uuidValidate(metaMetricsId)) {\n if (uuidVersion(metaMetricsId) !== 4) {\n throw new Error(\n `Invalid UUID version. Expected v4, got v${uuidVersion(metaMetricsId)}`,\n );\n }\n idValue = uuidStringToBigInt(metaMetricsId) - MIN_UUID_V4_BIGINT;\n maxValue = UUID_V4_VALUE_RANGE_BIGINT;\n } else {\n // hex format with 0x prefix\n if (!metaMetricsId.startsWith('0x')) {\n throw new Error('Hex ID must start with 0x prefix');\n }\n\n const cleanId = metaMetricsId.slice(2);\n const EXPECTED_HEX_LENGTH = 64; // 32 bytes = 64 hex characters\n\n if (cleanId.length !== EXPECTED_HEX_LENGTH) {\n throw new Error(\n `Invalid hex ID length. Expected ${EXPECTED_HEX_LENGTH} characters, got ${cleanId.length}`,\n );\n }\n\n if (!/^[0-9a-f]+$/iu.test(cleanId)) {\n throw new Error('Hex ID contains invalid characters');\n }\n\n idValue = BigInt(`0x${cleanId}`);\n maxValue = BigInt(`0x${'f'.repeat(cleanId.length)}`);\n }\n\n // Use BigInt division first, then convert to number to maintain precision\n return Number((idValue * BigInt(1_000_000)) / maxValue) / 1_000_000;\n}\n\n/**\n * Type guard to check if a value is a feature flag with scope.\n * Used to validate feature flag objects that contain scope-based configurations.\n *\n * @param featureFlag - The value to check if it's a feature flag with scope\n * @returns True if the value is a feature flag with scope, false otherwise\n */\nexport const isFeatureFlagWithScopeValue = (\n featureFlag: Json,\n): featureFlag is FeatureFlagScopeValue => {\n return (\n typeof featureFlag === 'object' &&\n featureFlag !== null &&\n 'scope' in featureFlag\n );\n};\n"]}
@@ -4,9 +4,11 @@ import type { FeatureFlagScopeValue } from "../remote-feature-flag-controller-ty
4
4
  * Generates a deterministic random number between 0 and 1 based on a metaMetricsId.
5
5
  * This is useful for A/B testing and feature flag rollouts where we want
6
6
  * consistent group assignment for the same user.
7
- *
8
- * @param metaMetricsId - The unique identifier used to generate the deterministic random number
9
- * @returns A number between 0 and 1 that is deterministic for the given metaMetricsId
7
+ * @param metaMetricsId - The unique identifier used to generate the deterministic random number. Must be either:
8
+ * - A UUIDv4 string (e.g., '123e4567-e89b-12d3-a456-426614174000'
9
+ * - A hex string with '0x' prefix (e.g., '0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420')
10
+ * @returns A number between 0 and 1, deterministically generated from the input ID.
11
+ * The same input will always produce the same output.
10
12
  */
11
13
  export declare function generateDeterministicRandomNumber(metaMetricsId: string): number;
12
14
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"user-segmentation-utils.d.cts","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAE5C,OAAO,KAAK,EAAE,qBAAqB,EAAE,oDAAgD;AAGrF;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAC/C,aAAa,EAAE,MAAM,GACpB,MAAM,CAOR;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,gBACzB,IAAI,yCAOlB,CAAC"}
1
+ {"version":3,"file":"user-segmentation-utils.d.cts","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAG5C,OAAO,KAAK,EAAE,qBAAqB,EAAE,oDAAgD;AAiBrF;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAC/C,aAAa,EAAE,MAAM,GACpB,MAAM,CA0CR;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,gBACzB,IAAI,yCAOlB,CAAC"}
@@ -4,9 +4,11 @@ import type { FeatureFlagScopeValue } from "../remote-feature-flag-controller-ty
4
4
  * Generates a deterministic random number between 0 and 1 based on a metaMetricsId.
5
5
  * This is useful for A/B testing and feature flag rollouts where we want
6
6
  * consistent group assignment for the same user.
7
- *
8
- * @param metaMetricsId - The unique identifier used to generate the deterministic random number
9
- * @returns A number between 0 and 1 that is deterministic for the given metaMetricsId
7
+ * @param metaMetricsId - The unique identifier used to generate the deterministic random number. Must be either:
8
+ * - A UUIDv4 string (e.g., '123e4567-e89b-12d3-a456-426614174000'
9
+ * - A hex string with '0x' prefix (e.g., '0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420')
10
+ * @returns A number between 0 and 1, deterministically generated from the input ID.
11
+ * The same input will always produce the same output.
10
12
  */
11
13
  export declare function generateDeterministicRandomNumber(metaMetricsId: string): number;
12
14
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"user-segmentation-utils.d.mts","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAE5C,OAAO,KAAK,EAAE,qBAAqB,EAAE,oDAAgD;AAGrF;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAC/C,aAAa,EAAE,MAAM,GACpB,MAAM,CAOR;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,gBACzB,IAAI,yCAOlB,CAAC"}
1
+ {"version":3,"file":"user-segmentation-utils.d.mts","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAG5C,OAAO,KAAK,EAAE,qBAAqB,EAAE,oDAAgD;AAiBrF;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAC/C,aAAa,EAAE,MAAM,GACpB,MAAM,CA0CR;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,gBACzB,IAAI,yCAOlB,CAAC"}
@@ -1,18 +1,59 @@
1
- /* eslint-disable no-bitwise */
1
+ import { validate as uuidValidate, version as uuidVersion } from "uuid";
2
+ /**
3
+ * Converts a UUID string to a BigInt by removing dashes and converting to hexadecimal.
4
+ * @param uuid - The UUID string to convert
5
+ * @returns The UUID as a BigInt value
6
+ */
7
+ function uuidStringToBigInt(uuid) {
8
+ return BigInt(`0x${uuid.replace(/-/gu, '')}`);
9
+ }
10
+ const MIN_UUID_V4 = '00000000-0000-4000-8000-000000000000';
11
+ const MAX_UUID_V4 = 'ffffffff-ffff-4fff-bfff-ffffffffffff';
12
+ const MIN_UUID_V4_BIGINT = uuidStringToBigInt(MIN_UUID_V4);
13
+ const MAX_UUID_V4_BIGINT = uuidStringToBigInt(MAX_UUID_V4);
14
+ const UUID_V4_VALUE_RANGE_BIGINT = MAX_UUID_V4_BIGINT - MIN_UUID_V4_BIGINT;
2
15
  /**
3
16
  * Generates a deterministic random number between 0 and 1 based on a metaMetricsId.
4
17
  * This is useful for A/B testing and feature flag rollouts where we want
5
18
  * consistent group assignment for the same user.
6
- *
7
- * @param metaMetricsId - The unique identifier used to generate the deterministic random number
8
- * @returns A number between 0 and 1 that is deterministic for the given metaMetricsId
19
+ * @param metaMetricsId - The unique identifier used to generate the deterministic random number. Must be either:
20
+ * - A UUIDv4 string (e.g., '123e4567-e89b-12d3-a456-426614174000'
21
+ * - A hex string with '0x' prefix (e.g., '0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420')
22
+ * @returns A number between 0 and 1, deterministically generated from the input ID.
23
+ * The same input will always produce the same output.
9
24
  */
10
25
  export function generateDeterministicRandomNumber(metaMetricsId) {
11
- const hash = [...metaMetricsId].reduce((acc, char) => {
12
- const chr = char.charCodeAt(0);
13
- return ((acc << 5) - acc + chr) | 0;
14
- }, 0);
15
- return (hash >>> 0) / 0xffffffff;
26
+ if (!metaMetricsId) {
27
+ throw new Error('MetaMetrics ID cannot be empty');
28
+ }
29
+ let idValue;
30
+ let maxValue;
31
+ // uuidv4 format
32
+ if (uuidValidate(metaMetricsId)) {
33
+ if (uuidVersion(metaMetricsId) !== 4) {
34
+ throw new Error(`Invalid UUID version. Expected v4, got v${uuidVersion(metaMetricsId)}`);
35
+ }
36
+ idValue = uuidStringToBigInt(metaMetricsId) - MIN_UUID_V4_BIGINT;
37
+ maxValue = UUID_V4_VALUE_RANGE_BIGINT;
38
+ }
39
+ else {
40
+ // hex format with 0x prefix
41
+ if (!metaMetricsId.startsWith('0x')) {
42
+ throw new Error('Hex ID must start with 0x prefix');
43
+ }
44
+ const cleanId = metaMetricsId.slice(2);
45
+ const EXPECTED_HEX_LENGTH = 64; // 32 bytes = 64 hex characters
46
+ if (cleanId.length !== EXPECTED_HEX_LENGTH) {
47
+ throw new Error(`Invalid hex ID length. Expected ${EXPECTED_HEX_LENGTH} characters, got ${cleanId.length}`);
48
+ }
49
+ if (!/^[0-9a-f]+$/iu.test(cleanId)) {
50
+ throw new Error('Hex ID contains invalid characters');
51
+ }
52
+ idValue = BigInt(`0x${cleanId}`);
53
+ maxValue = BigInt(`0x${'f'.repeat(cleanId.length)}`);
54
+ }
55
+ // Use BigInt division first, then convert to number to maintain precision
56
+ return Number((idValue * BigInt(1000000)) / maxValue) / 1000000;
16
57
  }
17
58
  /**
18
59
  * Type guard to check if a value is a feature flag with scope.
@@ -1 +1 @@
1
- {"version":3,"file":"user-segmentation-utils.mjs","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":"AAIA,+BAA+B;AAC/B;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC,CAC/C,aAAqB;IAErB,MAAM,IAAI,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC/B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEN,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,GAAG,UAAU,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,WAAiB,EACqB,EAAE;IACxC,OAAO,CACL,OAAO,WAAW,KAAK,QAAQ;QAC/B,WAAW,KAAK,IAAI;QACpB,OAAO,IAAI,WAAW,CACvB,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import type { Json } from '@metamask/utils';\n\nimport type { FeatureFlagScopeValue } from '../remote-feature-flag-controller-types';\n\n/* eslint-disable no-bitwise */\n/**\n * Generates a deterministic random number between 0 and 1 based on a metaMetricsId.\n * This is useful for A/B testing and feature flag rollouts where we want\n * consistent group assignment for the same user.\n *\n * @param metaMetricsId - The unique identifier used to generate the deterministic random number\n * @returns A number between 0 and 1 that is deterministic for the given metaMetricsId\n */\nexport function generateDeterministicRandomNumber(\n metaMetricsId: string,\n): number {\n const hash = [...metaMetricsId].reduce((acc, char) => {\n const chr = char.charCodeAt(0);\n return ((acc << 5) - acc + chr) | 0;\n }, 0);\n\n return (hash >>> 0) / 0xffffffff;\n}\n\n/**\n * Type guard to check if a value is a feature flag with scope.\n * Used to validate feature flag objects that contain scope-based configurations.\n *\n * @param featureFlag - The value to check if it's a feature flag with scope\n * @returns True if the value is a feature flag with scope, false otherwise\n */\nexport const isFeatureFlagWithScopeValue = (\n featureFlag: Json,\n): featureFlag is FeatureFlagScopeValue => {\n return (\n typeof featureFlag === 'object' &&\n featureFlag !== null &&\n 'scope' in featureFlag\n );\n};\n"]}
1
+ {"version":3,"file":"user-segmentation-utils.mjs","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,OAAO,IAAI,WAAW,EAAE,aAAa;AAIxE;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,IAAY;IACtC,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,WAAW,GAAG,sCAAsC,CAAC;AAC3D,MAAM,WAAW,GAAG,sCAAsC,CAAC;AAC3D,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;AAC3D,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;AAC3D,MAAM,0BAA0B,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAE3E;;;;;;;;;GASG;AACH,MAAM,UAAU,iCAAiC,CAC/C,aAAqB;IAErB,IAAI,CAAC,aAAa,EAAE;QAClB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;KACnD;IAED,IAAI,OAAe,CAAC;IACpB,IAAI,QAAgB,CAAC;IAErB,gBAAgB;IAChB,IAAI,YAAY,CAAC,aAAa,CAAC,EAAE;QAC/B,IAAI,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE;YACpC,MAAM,IAAI,KAAK,CACb,2CAA2C,WAAW,CAAC,aAAa,CAAC,EAAE,CACxE,CAAC;SACH;QACD,OAAO,GAAG,kBAAkB,CAAC,aAAa,CAAC,GAAG,kBAAkB,CAAC;QACjE,QAAQ,GAAG,0BAA0B,CAAC;KACvC;SAAM;QACL,4BAA4B;QAC5B,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;YACnC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;SACrD;QAED,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,mBAAmB,GAAG,EAAE,CAAC,CAAC,+BAA+B;QAE/D,IAAI,OAAO,CAAC,MAAM,KAAK,mBAAmB,EAAE;YAC1C,MAAM,IAAI,KAAK,CACb,mCAAmC,mBAAmB,oBAAoB,OAAO,CAAC,MAAM,EAAE,CAC3F,CAAC;SACH;QAED,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YAClC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;SACvD;QAED,OAAO,GAAG,MAAM,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;QACjC,QAAQ,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;KACtD;IAED,0EAA0E;IAC1E,OAAO,MAAM,CAAC,CAAC,OAAO,GAAG,MAAM,CAAC,OAAS,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,OAAS,CAAC;AACtE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,WAAiB,EACqB,EAAE;IACxC,OAAO,CACL,OAAO,WAAW,KAAK,QAAQ;QAC/B,WAAW,KAAK,IAAI;QACpB,OAAO,IAAI,WAAW,CACvB,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import type { Json } from '@metamask/utils';\nimport { validate as uuidValidate, version as uuidVersion } from 'uuid';\n\nimport type { FeatureFlagScopeValue } from '../remote-feature-flag-controller-types';\n\n/**\n * Converts a UUID string to a BigInt by removing dashes and converting to hexadecimal.\n * @param uuid - The UUID string to convert\n * @returns The UUID as a BigInt value\n */\nfunction uuidStringToBigInt(uuid: string): bigint {\n return BigInt(`0x${uuid.replace(/-/gu, '')}`);\n}\n\nconst MIN_UUID_V4 = '00000000-0000-4000-8000-000000000000';\nconst MAX_UUID_V4 = 'ffffffff-ffff-4fff-bfff-ffffffffffff';\nconst MIN_UUID_V4_BIGINT = uuidStringToBigInt(MIN_UUID_V4);\nconst MAX_UUID_V4_BIGINT = uuidStringToBigInt(MAX_UUID_V4);\nconst UUID_V4_VALUE_RANGE_BIGINT = MAX_UUID_V4_BIGINT - MIN_UUID_V4_BIGINT;\n\n/**\n * Generates a deterministic random number between 0 and 1 based on a metaMetricsId.\n * This is useful for A/B testing and feature flag rollouts where we want\n * consistent group assignment for the same user.\n * @param metaMetricsId - The unique identifier used to generate the deterministic random number. Must be either:\n * - A UUIDv4 string (e.g., '123e4567-e89b-12d3-a456-426614174000'\n * - A hex string with '0x' prefix (e.g., '0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420')\n * @returns A number between 0 and 1, deterministically generated from the input ID.\n * The same input will always produce the same output.\n */\nexport function generateDeterministicRandomNumber(\n metaMetricsId: string,\n): number {\n if (!metaMetricsId) {\n throw new Error('MetaMetrics ID cannot be empty');\n }\n\n let idValue: bigint;\n let maxValue: bigint;\n\n // uuidv4 format\n if (uuidValidate(metaMetricsId)) {\n if (uuidVersion(metaMetricsId) !== 4) {\n throw new Error(\n `Invalid UUID version. Expected v4, got v${uuidVersion(metaMetricsId)}`,\n );\n }\n idValue = uuidStringToBigInt(metaMetricsId) - MIN_UUID_V4_BIGINT;\n maxValue = UUID_V4_VALUE_RANGE_BIGINT;\n } else {\n // hex format with 0x prefix\n if (!metaMetricsId.startsWith('0x')) {\n throw new Error('Hex ID must start with 0x prefix');\n }\n\n const cleanId = metaMetricsId.slice(2);\n const EXPECTED_HEX_LENGTH = 64; // 32 bytes = 64 hex characters\n\n if (cleanId.length !== EXPECTED_HEX_LENGTH) {\n throw new Error(\n `Invalid hex ID length. Expected ${EXPECTED_HEX_LENGTH} characters, got ${cleanId.length}`,\n );\n }\n\n if (!/^[0-9a-f]+$/iu.test(cleanId)) {\n throw new Error('Hex ID contains invalid characters');\n }\n\n idValue = BigInt(`0x${cleanId}`);\n maxValue = BigInt(`0x${'f'.repeat(cleanId.length)}`);\n }\n\n // Use BigInt division first, then convert to number to maintain precision\n return Number((idValue * BigInt(1_000_000)) / maxValue) / 1_000_000;\n}\n\n/**\n * Type guard to check if a value is a feature flag with scope.\n * Used to validate feature flag objects that contain scope-based configurations.\n *\n * @param featureFlag - The value to check if it's a feature flag with scope\n * @returns True if the value is a feature flag with scope, false otherwise\n */\nexport const isFeatureFlagWithScopeValue = (\n featureFlag: Json,\n): featureFlag is FeatureFlagScopeValue => {\n return (\n typeof featureFlag === 'object' &&\n featureFlag !== null &&\n 'scope' in featureFlag\n );\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/remote-feature-flag-controller",
3
- "version": "1.2.0-preview-2ebcbae7",
3
+ "version": "1.2.0-preview-ae144301",
4
4
  "description": "The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags",
5
5
  "keywords": [
6
6
  "MetaMask",