@metamask-previews/remote-feature-flag-controller 5.0.0-preview-6b4bcdab3 → 5.0.0-preview-0b5bfc2

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.
@@ -2,15 +2,15 @@ import type { Json } from "@metamask/utils";
2
2
  import type { FeatureFlagScopeValue } from "../remote-feature-flag-controller-types.mjs";
3
3
  /**
4
4
  * Calculates a deterministic threshold value between 0 and 1 for A/B testing.
5
- * This function hashes the user's MetaMetrics ID combined with the feature flag name
5
+ * This function hashes the segmentation ID combined with the feature flag name
6
6
  * to ensure consistent group assignment across sessions while varying across different flags.
7
7
  *
8
- * @param metaMetricsId - The user's MetaMetrics ID (must be non-empty)
8
+ * @param segmentationId - The identifier used for threshold segmentation (must be non-empty)
9
9
  * @param featureFlagName - The feature flag name to create unique threshold per flag
10
10
  * @returns A promise that resolves to a number between 0 and 1
11
- * @throws Error if metaMetricsId is empty
11
+ * @throws Error if segmentationId is empty
12
12
  */
13
- export declare function calculateThresholdForFlag(metaMetricsId: string, featureFlagName: string): Promise<number>;
13
+ export declare function calculateThresholdForFlag(segmentationId: string, featureFlagName: string): Promise<number>;
14
14
  /**
15
15
  * Generates a deterministic random number between 0 and 1 based on a metaMetricsId.
16
16
  * This is useful for A/B testing and feature flag rollouts where we want
@@ -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;AAI5C,OAAO,KAAK,EAAE,qBAAqB,EAAE,oDAAmD;AAkBxF;;;;;;;;;GASG;AACH,wBAAsB,yBAAyB,CAC7C,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,CAAC,CAsBjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iCAAiC,CAC/C,aAAa,EAAE,MAAM,GACpB,MAAM,CA0CR;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;AAI5C,OAAO,KAAK,EAAE,qBAAqB,EAAE,oDAAmD;AAkBxF;;;;;;;;;GASG;AACH,wBAAsB,yBAAyB,CAC7C,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,CAAC,CAsBjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iCAAiC,CAC/C,aAAa,EAAE,MAAM,GACpB,MAAM,CA0CR;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,gBACzB,IAAI,yCAOlB,CAAC"}
@@ -16,22 +16,22 @@ const MAX_UUID_V4_BIGINT = uuidStringToBigInt(MAX_UUID_V4);
16
16
  const UUID_V4_VALUE_RANGE_BIGINT = MAX_UUID_V4_BIGINT - MIN_UUID_V4_BIGINT;
17
17
  /**
18
18
  * Calculates a deterministic threshold value between 0 and 1 for A/B testing.
19
- * This function hashes the user's MetaMetrics ID combined with the feature flag name
19
+ * This function hashes the segmentation ID combined with the feature flag name
20
20
  * to ensure consistent group assignment across sessions while varying across different flags.
21
21
  *
22
- * @param metaMetricsId - The user's MetaMetrics ID (must be non-empty)
22
+ * @param segmentationId - The identifier used for threshold segmentation (must be non-empty)
23
23
  * @param featureFlagName - The feature flag name to create unique threshold per flag
24
24
  * @returns A promise that resolves to a number between 0 and 1
25
- * @throws Error if metaMetricsId is empty
25
+ * @throws Error if segmentationId is empty
26
26
  */
27
- export async function calculateThresholdForFlag(metaMetricsId, featureFlagName) {
28
- if (!metaMetricsId) {
29
- throw new Error('MetaMetrics ID cannot be empty');
27
+ export async function calculateThresholdForFlag(segmentationId, featureFlagName) {
28
+ if (!segmentationId) {
29
+ throw new Error('Segmentation ID cannot be empty');
30
30
  }
31
31
  if (!featureFlagName) {
32
32
  throw new Error('Feature flag name cannot be empty');
33
33
  }
34
- const seed = metaMetricsId + featureFlagName;
34
+ const seed = segmentationId + featureFlagName;
35
35
  // Hash the combined seed
36
36
  const encoder = new TextEncoder();
37
37
  const hashBuffer = await sha256(encoder.encode(seed));
@@ -1 +1 @@
1
- {"version":3,"file":"user-segmentation-utils.mjs","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Json } from '@metamask/utils';\nimport { sha256, bytesToHex } from '@metamask/utils';\nimport { validate as uuidValidate, version as uuidVersion } from 'uuid';\n\nimport type { FeatureFlagScopeValue } from '../remote-feature-flag-controller-types.js';\n\n/**\n * Converts a UUID string to a BigInt by removing dashes and converting to hexadecimal.\n *\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 * Calculates a deterministic threshold value between 0 and 1 for A/B testing.\n * This function hashes the user's MetaMetrics ID combined with the feature flag name\n * to ensure consistent group assignment across sessions while varying across different flags.\n *\n * @param metaMetricsId - The user's MetaMetrics ID (must be non-empty)\n * @param featureFlagName - The feature flag name to create unique threshold per flag\n * @returns A promise that resolves to a number between 0 and 1\n * @throws Error if metaMetricsId is empty\n */\nexport async function calculateThresholdForFlag(\n metaMetricsId: string,\n featureFlagName: string,\n): Promise<number> {\n if (!metaMetricsId) {\n throw new Error('MetaMetrics ID cannot be empty');\n }\n\n if (!featureFlagName) {\n throw new Error('Feature flag name cannot be empty');\n }\n\n const seed = metaMetricsId + featureFlagName;\n\n // Hash the combined seed\n const encoder = new TextEncoder();\n const hashBuffer = await sha256(encoder.encode(seed));\n\n // Convert hash bytes directly to 0-1 range\n const hash = bytesToHex(hashBuffer);\n const hashBigInt = BigInt(hash);\n const maxValue = BigInt(`0x${'f'.repeat(64)}`);\n\n // Use BigInt division first, then convert to number to maintain precision\n return Number((hashBigInt * BigInt(1_000_000)) / maxValue) / 1_000_000;\n}\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 *\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"]}
1
+ {"version":3,"file":"user-segmentation-utils.mjs","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Json } from '@metamask/utils';\nimport { sha256, bytesToHex } from '@metamask/utils';\nimport { validate as uuidValidate, version as uuidVersion } from 'uuid';\n\nimport type { FeatureFlagScopeValue } from '../remote-feature-flag-controller-types.js';\n\n/**\n * Converts a UUID string to a BigInt by removing dashes and converting to hexadecimal.\n *\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 * Calculates a deterministic threshold value between 0 and 1 for A/B testing.\n * This function hashes the segmentation ID combined with the feature flag name\n * to ensure consistent group assignment across sessions while varying across different flags.\n *\n * @param segmentationId - The identifier used for threshold segmentation (must be non-empty)\n * @param featureFlagName - The feature flag name to create unique threshold per flag\n * @returns A promise that resolves to a number between 0 and 1\n * @throws Error if segmentationId is empty\n */\nexport async function calculateThresholdForFlag(\n segmentationId: string,\n featureFlagName: string,\n): Promise<number> {\n if (!segmentationId) {\n throw new Error('Segmentation ID cannot be empty');\n }\n\n if (!featureFlagName) {\n throw new Error('Feature flag name cannot be empty');\n }\n\n const seed = segmentationId + featureFlagName;\n\n // Hash the combined seed\n const encoder = new TextEncoder();\n const hashBuffer = await sha256(encoder.encode(seed));\n\n // Convert hash bytes directly to 0-1 range\n const hash = bytesToHex(hashBuffer);\n const hashBigInt = BigInt(hash);\n const maxValue = BigInt(`0x${'f'.repeat(64)}`);\n\n // Use BigInt division first, then convert to number to maintain precision\n return Number((hashBigInt * BigInt(1_000_000)) / maxValue) / 1_000_000;\n}\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 *\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": "5.0.0-preview-6b4bcdab3",
3
+ "version": "5.0.0-preview-0b5bfc2",
4
4
  "description": "The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags",
5
5
  "keywords": [
6
6
  "Ethereum",