@metamask-previews/remote-feature-flag-controller 1.2.0-preview-4e54ae1 → 1.3.0-preview-298fc547
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 +6 -1
- package/dist/utils/user-segmentation-utils.cjs +50 -9
- package/dist/utils/user-segmentation-utils.cjs.map +1 -1
- package/dist/utils/user-segmentation-utils.d.cts +5 -3
- package/dist/utils/user-segmentation-utils.d.cts.map +1 -1
- package/dist/utils/user-segmentation-utils.d.mts +5 -3
- package/dist/utils/user-segmentation-utils.d.mts.map +1 -1
- package/dist/utils/user-segmentation-utils.mjs +50 -9
- package/dist/utils/user-segmentation-utils.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,8 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.0]
|
|
11
|
+
|
|
10
12
|
### Changed
|
|
11
13
|
|
|
14
|
+
- Improve user segmentation with BigInt-based random generation ([#5110](https://github.com/MetaMask/core/pull/5110))
|
|
15
|
+
- Change getMetaMetricsId to only sync func type ([#5108](https://github.com/MetaMask/core/pull/5108))
|
|
12
16
|
- Bump `@metamask/base-controller` from `^7.0.0` to `^7.1.0` ([#5079](https://github.com/MetaMask/core/pull/5079))
|
|
13
17
|
|
|
14
18
|
## [1.2.0]
|
|
@@ -38,7 +42,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
38
42
|
- Initial release of the RemoteFeatureFlagController. ([#4931](https://github.com/MetaMask/core/pull/4931))
|
|
39
43
|
- This controller manages the retrieval and caching of remote feature flags. It fetches feature flags from a remote API, caches them, and provides methods to access and manage these flags. The controller ensures that feature flags are refreshed based on a specified interval and handles cases where the controller is disabled or the network is unavailable.
|
|
40
44
|
|
|
41
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@1.
|
|
45
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@1.3.0...HEAD
|
|
46
|
+
[1.3.0]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@1.2.0...@metamask/remote-feature-flag-controller@1.3.0
|
|
42
47
|
[1.2.0]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@1.1.0...@metamask/remote-feature-flag-controller@1.2.0
|
|
43
48
|
[1.1.0]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@1.0.0...@metamask/remote-feature-flag-controller@1.1.0
|
|
44
49
|
[1.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/remote-feature-flag-controller@1.0.0
|
|
@@ -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
|
-
|
|
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
|
-
*
|
|
11
|
-
*
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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":";;;
|
|
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
|
-
*
|
|
9
|
-
*
|
|
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;
|
|
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
|
-
*
|
|
9
|
-
*
|
|
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;
|
|
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
|
-
|
|
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
|
-
*
|
|
8
|
-
*
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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":"
|
|
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.
|
|
3
|
+
"version": "1.3.0-preview-298fc547",
|
|
4
4
|
"description": "The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|