@metamask-previews/remote-feature-flag-controller 1.1.0-preview-5db6a424 → 1.2.0-preview-cdf3e01b
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 +10 -1
- package/dist/remote-feature-flag-controller-types.cjs.map +1 -1
- package/dist/remote-feature-flag-controller-types.d.cts +9 -0
- package/dist/remote-feature-flag-controller-types.d.cts.map +1 -1
- package/dist/remote-feature-flag-controller-types.d.mts +9 -0
- package/dist/remote-feature-flag-controller-types.d.mts.map +1 -1
- package/dist/remote-feature-flag-controller-types.mjs.map +1 -1
- package/dist/remote-feature-flag-controller.cjs +41 -6
- package/dist/remote-feature-flag-controller.cjs.map +1 -1
- package/dist/remote-feature-flag-controller.d.cts +3 -1
- package/dist/remote-feature-flag-controller.d.cts.map +1 -1
- package/dist/remote-feature-flag-controller.d.mts +3 -1
- package/dist/remote-feature-flag-controller.d.mts.map +1 -1
- package/dist/remote-feature-flag-controller.mjs +41 -6
- package/dist/remote-feature-flag-controller.mjs.map +1 -1
- package/dist/utils/user-segmentation-utils.cjs +43 -0
- package/dist/utils/user-segmentation-utils.cjs.map +1 -0
- package/dist/utils/user-segmentation-utils.d.cts +25 -0
- package/dist/utils/user-segmentation-utils.d.cts.map +1 -0
- package/dist/utils/user-segmentation-utils.d.mts +25 -0
- package/dist/utils/user-segmentation-utils.d.mts.map +1 -0
- package/dist/utils/user-segmentation-utils.mjs +37 -0
- package/dist/utils/user-segmentation-utils.mjs.map +1 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added support for threshold-based feature flag scoping ([#5051](https://github.com/MetaMask/core/pull/5051))
|
|
15
|
+
- Enables percentage-based feature flag distribution across user base
|
|
16
|
+
- Uses deterministic random group assignment based on metaMetricsId from the client
|
|
17
|
+
|
|
10
18
|
## [1.1.0]
|
|
11
19
|
|
|
12
20
|
### Added
|
|
@@ -26,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
26
34
|
- Initial release of the RemoteFeatureFlagController. ([#4931](https://github.com/MetaMask/core/pull/4931))
|
|
27
35
|
- 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.
|
|
28
36
|
|
|
29
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@1.
|
|
37
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@1.2.0...HEAD
|
|
38
|
+
[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
|
|
30
39
|
[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
|
|
31
40
|
[1.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/remote-feature-flag-controller@1.0.0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller-types.cjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller-types.ts"],"names":[],"mappings":";;;AAGA,mEAAmE;AACnE,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,qCAAuB,CAAA;IACvB,+BAAiB,CAAA;AACnB,CAAC,EAHW,UAAU,0BAAV,UAAU,QAGrB;AAED,IAAY,gBAGX;AAHD,WAAY,gBAAgB;IAC1B,iCAAa,CAAA;IACb,mCAAe,CAAA;AACjB,CAAC,EAHW,gBAAgB,gCAAhB,gBAAgB,QAG3B;AAED,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,sCAAmB,CAAA;IACnB,0CAAuB,CAAA;IACvB,sCAAmB,CAAA;AACrB,CAAC,EAJW,eAAe,+BAAf,eAAe,QAI1B","sourcesContent":["import type { ControllerGetStateAction } from '@metamask/base-controller';\nimport type { Json } from '@metamask/utils';\n\n// Define accepted values for client, distribution, and environment\nexport enum ClientType {\n Extension = 'extension',\n Mobile = 'mobile',\n}\n\nexport enum DistributionType {\n Main = 'main',\n Flask = 'flask',\n}\n\nexport enum EnvironmentType {\n Production = 'prod',\n ReleaseCandidate = 'rc',\n Development = 'dev',\n}\n\n/** Type representing the feature flags collection */\nexport type FeatureFlags = {\n [key: string]: Json;\n};\n\nexport type ApiDataResponse = FeatureFlags[];\n\nexport type ServiceResponse = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number | null;\n};\n\n/**\n * Describes the shape of the state object for the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerState = {\n /**\n * The collection of feature flags and their respective values, which can be objects.\n */\n remoteFeatureFlags: FeatureFlags;\n /**\n * The timestamp of the last successful feature flag cache.\n */\n cacheTimestamp: number;\n};\n\n/**\n * The action to retrieve the state of the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n 'RemoteFeatureFlagController',\n RemoteFeatureFlagControllerState\n >;\n"]}
|
|
1
|
+
{"version":3,"file":"remote-feature-flag-controller-types.cjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller-types.ts"],"names":[],"mappings":";;;AAGA,mEAAmE;AACnE,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,qCAAuB,CAAA;IACvB,+BAAiB,CAAA;AACnB,CAAC,EAHW,UAAU,0BAAV,UAAU,QAGrB;AAED,IAAY,gBAGX;AAHD,WAAY,gBAAgB;IAC1B,iCAAa,CAAA;IACb,mCAAe,CAAA;AACjB,CAAC,EAHW,gBAAgB,gCAAhB,gBAAgB,QAG3B;AAED,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,sCAAmB,CAAA;IACnB,0CAAuB,CAAA;IACvB,sCAAmB,CAAA;AACrB,CAAC,EAJW,eAAe,+BAAf,eAAe,QAI1B","sourcesContent":["import type { ControllerGetStateAction } from '@metamask/base-controller';\nimport type { Json } from '@metamask/utils';\n\n// Define accepted values for client, distribution, and environment\nexport enum ClientType {\n Extension = 'extension',\n Mobile = 'mobile',\n}\n\nexport enum DistributionType {\n Main = 'main',\n Flask = 'flask',\n}\n\nexport enum EnvironmentType {\n Production = 'prod',\n ReleaseCandidate = 'rc',\n Development = 'dev',\n}\n\n/** Type representing the feature flags collection */\nexport type FeatureFlags = {\n [key: string]: Json;\n};\n\nexport type FeatureFlagScope = {\n type: string;\n value: number;\n};\n\nexport type FeatureFlagScopeValue = {\n name: string;\n scope: FeatureFlagScope;\n value: Json;\n};\n\nexport type ApiDataResponse = FeatureFlags[];\n\nexport type ServiceResponse = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number | null;\n};\n\n/**\n * Describes the shape of the state object for the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerState = {\n /**\n * The collection of feature flags and their respective values, which can be objects.\n */\n remoteFeatureFlags: FeatureFlags;\n /**\n * The timestamp of the last successful feature flag cache.\n */\n cacheTimestamp: number;\n};\n\n/**\n * The action to retrieve the state of the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n 'RemoteFeatureFlagController',\n RemoteFeatureFlagControllerState\n >;\n"]}
|
|
@@ -17,6 +17,15 @@ export declare enum EnvironmentType {
|
|
|
17
17
|
export type FeatureFlags = {
|
|
18
18
|
[key: string]: Json;
|
|
19
19
|
};
|
|
20
|
+
export type FeatureFlagScope = {
|
|
21
|
+
type: string;
|
|
22
|
+
value: number;
|
|
23
|
+
};
|
|
24
|
+
export type FeatureFlagScopeValue = {
|
|
25
|
+
name: string;
|
|
26
|
+
scope: FeatureFlagScope;
|
|
27
|
+
value: Json;
|
|
28
|
+
};
|
|
20
29
|
export type ApiDataResponse = FeatureFlags[];
|
|
21
30
|
export type ServiceResponse = {
|
|
22
31
|
remoteFeatureFlags: FeatureFlags;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller-types.d.cts","sourceRoot":"","sources":["../src/remote-feature-flag-controller-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,kCAAkC;AAC1E,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAG5C,oBAAY,UAAU;IACpB,SAAS,cAAc;IACvB,MAAM,WAAW;CAClB;AAED,oBAAY,gBAAgB;IAC1B,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED,oBAAY,eAAe;IACzB,UAAU,SAAS;IACnB,gBAAgB,OAAO;IACvB,WAAW,QAAQ;CACpB;AAED,qDAAqD;AACrD,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,YAAY,EAAE,CAAC;AAE7C,MAAM,MAAM,eAAe,GAAG;IAC5B,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;OAEG;IACH,kBAAkB,EAAE,YAAY,CAAC;IACjC;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,6BAA6B,EAC7B,gCAAgC,CACjC,CAAC"}
|
|
1
|
+
{"version":3,"file":"remote-feature-flag-controller-types.d.cts","sourceRoot":"","sources":["../src/remote-feature-flag-controller-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,kCAAkC;AAC1E,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAG5C,oBAAY,UAAU;IACpB,SAAS,cAAc;IACvB,MAAM,WAAW;CAClB;AAED,oBAAY,gBAAgB;IAC1B,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED,oBAAY,eAAe;IACzB,UAAU,SAAS;IACnB,gBAAgB,OAAO;IACvB,WAAW,QAAQ;CACpB;AAED,qDAAqD;AACrD,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,gBAAgB,CAAC;IACxB,KAAK,EAAE,IAAI,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,YAAY,EAAE,CAAC;AAE7C,MAAM,MAAM,eAAe,GAAG;IAC5B,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;OAEG;IACH,kBAAkB,EAAE,YAAY,CAAC;IACjC;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,6BAA6B,EAC7B,gCAAgC,CACjC,CAAC"}
|
|
@@ -17,6 +17,15 @@ export declare enum EnvironmentType {
|
|
|
17
17
|
export type FeatureFlags = {
|
|
18
18
|
[key: string]: Json;
|
|
19
19
|
};
|
|
20
|
+
export type FeatureFlagScope = {
|
|
21
|
+
type: string;
|
|
22
|
+
value: number;
|
|
23
|
+
};
|
|
24
|
+
export type FeatureFlagScopeValue = {
|
|
25
|
+
name: string;
|
|
26
|
+
scope: FeatureFlagScope;
|
|
27
|
+
value: Json;
|
|
28
|
+
};
|
|
20
29
|
export type ApiDataResponse = FeatureFlags[];
|
|
21
30
|
export type ServiceResponse = {
|
|
22
31
|
remoteFeatureFlags: FeatureFlags;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller-types.d.mts","sourceRoot":"","sources":["../src/remote-feature-flag-controller-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,kCAAkC;AAC1E,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAG5C,oBAAY,UAAU;IACpB,SAAS,cAAc;IACvB,MAAM,WAAW;CAClB;AAED,oBAAY,gBAAgB;IAC1B,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED,oBAAY,eAAe;IACzB,UAAU,SAAS;IACnB,gBAAgB,OAAO;IACvB,WAAW,QAAQ;CACpB;AAED,qDAAqD;AACrD,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,YAAY,EAAE,CAAC;AAE7C,MAAM,MAAM,eAAe,GAAG;IAC5B,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;OAEG;IACH,kBAAkB,EAAE,YAAY,CAAC;IACjC;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,6BAA6B,EAC7B,gCAAgC,CACjC,CAAC"}
|
|
1
|
+
{"version":3,"file":"remote-feature-flag-controller-types.d.mts","sourceRoot":"","sources":["../src/remote-feature-flag-controller-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,kCAAkC;AAC1E,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAG5C,oBAAY,UAAU;IACpB,SAAS,cAAc;IACvB,MAAM,WAAW;CAClB;AAED,oBAAY,gBAAgB;IAC1B,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED,oBAAY,eAAe;IACzB,UAAU,SAAS;IACnB,gBAAgB,OAAO;IACvB,WAAW,QAAQ;CACpB;AAED,qDAAqD;AACrD,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,gBAAgB,CAAC;IACxB,KAAK,EAAE,IAAI,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,YAAY,EAAE,CAAC;AAE7C,MAAM,MAAM,eAAe,GAAG;IAC5B,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;OAEG;IACH,kBAAkB,EAAE,YAAY,CAAC;IACjC;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,6BAA6B,EAC7B,gCAAgC,CACjC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller-types.mjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller-types.ts"],"names":[],"mappings":"AAGA,mEAAmE;AACnE,MAAM,CAAN,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,qCAAuB,CAAA;IACvB,+BAAiB,CAAA;AACnB,CAAC,EAHW,UAAU,KAAV,UAAU,QAGrB;AAED,MAAM,CAAN,IAAY,gBAGX;AAHD,WAAY,gBAAgB;IAC1B,iCAAa,CAAA;IACb,mCAAe,CAAA;AACjB,CAAC,EAHW,gBAAgB,KAAhB,gBAAgB,QAG3B;AAED,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,sCAAmB,CAAA;IACnB,0CAAuB,CAAA;IACvB,sCAAmB,CAAA;AACrB,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B","sourcesContent":["import type { ControllerGetStateAction } from '@metamask/base-controller';\nimport type { Json } from '@metamask/utils';\n\n// Define accepted values for client, distribution, and environment\nexport enum ClientType {\n Extension = 'extension',\n Mobile = 'mobile',\n}\n\nexport enum DistributionType {\n Main = 'main',\n Flask = 'flask',\n}\n\nexport enum EnvironmentType {\n Production = 'prod',\n ReleaseCandidate = 'rc',\n Development = 'dev',\n}\n\n/** Type representing the feature flags collection */\nexport type FeatureFlags = {\n [key: string]: Json;\n};\n\nexport type ApiDataResponse = FeatureFlags[];\n\nexport type ServiceResponse = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number | null;\n};\n\n/**\n * Describes the shape of the state object for the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerState = {\n /**\n * The collection of feature flags and their respective values, which can be objects.\n */\n remoteFeatureFlags: FeatureFlags;\n /**\n * The timestamp of the last successful feature flag cache.\n */\n cacheTimestamp: number;\n};\n\n/**\n * The action to retrieve the state of the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n 'RemoteFeatureFlagController',\n RemoteFeatureFlagControllerState\n >;\n"]}
|
|
1
|
+
{"version":3,"file":"remote-feature-flag-controller-types.mjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller-types.ts"],"names":[],"mappings":"AAGA,mEAAmE;AACnE,MAAM,CAAN,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,qCAAuB,CAAA;IACvB,+BAAiB,CAAA;AACnB,CAAC,EAHW,UAAU,KAAV,UAAU,QAGrB;AAED,MAAM,CAAN,IAAY,gBAGX;AAHD,WAAY,gBAAgB;IAC1B,iCAAa,CAAA;IACb,mCAAe,CAAA;AACjB,CAAC,EAHW,gBAAgB,KAAhB,gBAAgB,QAG3B;AAED,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,sCAAmB,CAAA;IACnB,0CAAuB,CAAA;IACvB,sCAAmB,CAAA;AACrB,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B","sourcesContent":["import type { ControllerGetStateAction } from '@metamask/base-controller';\nimport type { Json } from '@metamask/utils';\n\n// Define accepted values for client, distribution, and environment\nexport enum ClientType {\n Extension = 'extension',\n Mobile = 'mobile',\n}\n\nexport enum DistributionType {\n Main = 'main',\n Flask = 'flask',\n}\n\nexport enum EnvironmentType {\n Production = 'prod',\n ReleaseCandidate = 'rc',\n Development = 'dev',\n}\n\n/** Type representing the feature flags collection */\nexport type FeatureFlags = {\n [key: string]: Json;\n};\n\nexport type FeatureFlagScope = {\n type: string;\n value: number;\n};\n\nexport type FeatureFlagScopeValue = {\n name: string;\n scope: FeatureFlagScope;\n value: Json;\n};\n\nexport type ApiDataResponse = FeatureFlags[];\n\nexport type ServiceResponse = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number | null;\n};\n\n/**\n * Describes the shape of the state object for the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerState = {\n /**\n * The collection of feature flags and their respective values, which can be objects.\n */\n remoteFeatureFlags: FeatureFlags;\n /**\n * The timestamp of the last successful feature flag cache.\n */\n cacheTimestamp: number;\n};\n\n/**\n * The action to retrieve the state of the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n 'RemoteFeatureFlagController',\n RemoteFeatureFlagControllerState\n >;\n"]}
|
|
@@ -10,10 +10,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _RemoteFeatureFlagController_instances, _RemoteFeatureFlagController_fetchInterval, _RemoteFeatureFlagController_disabled, _RemoteFeatureFlagController_clientConfigApiService, _RemoteFeatureFlagController_inProgressFlagUpdate, _RemoteFeatureFlagController_isCacheExpired, _RemoteFeatureFlagController_updateCache;
|
|
13
|
+
var _RemoteFeatureFlagController_instances, _RemoteFeatureFlagController_fetchInterval, _RemoteFeatureFlagController_disabled, _RemoteFeatureFlagController_clientConfigApiService, _RemoteFeatureFlagController_inProgressFlagUpdate, _RemoteFeatureFlagController_getMetaMetricsId, _RemoteFeatureFlagController_isCacheExpired, _RemoteFeatureFlagController_updateCache, _RemoteFeatureFlagController_processRemoteFeatureFlags;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.RemoteFeatureFlagController = exports.getDefaultRemoteFeatureFlagControllerState = exports.DEFAULT_CACHE_DURATION = exports.controllerName = void 0;
|
|
16
16
|
const base_controller_1 = require("@metamask/base-controller");
|
|
17
|
+
const user_segmentation_utils_1 = require("./utils/user-segmentation-utils.cjs");
|
|
17
18
|
// === GENERAL ===
|
|
18
19
|
exports.controllerName = 'RemoteFeatureFlagController';
|
|
19
20
|
exports.DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day
|
|
@@ -49,8 +50,9 @@ class RemoteFeatureFlagController extends base_controller_1.BaseController {
|
|
|
49
50
|
* @param options.clientConfigApiService - The service instance to fetch remote feature flags.
|
|
50
51
|
* @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.
|
|
51
52
|
* @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.
|
|
53
|
+
* @param options.getMetaMetricsId - Promise that resolves to a metaMetricsId.
|
|
52
54
|
*/
|
|
53
|
-
constructor({ messenger, state, clientConfigApiService, fetchInterval = exports.DEFAULT_CACHE_DURATION, disabled = false, }) {
|
|
55
|
+
constructor({ messenger, state, clientConfigApiService, fetchInterval = exports.DEFAULT_CACHE_DURATION, disabled = false, getMetaMetricsId, }) {
|
|
54
56
|
super({
|
|
55
57
|
name: exports.controllerName,
|
|
56
58
|
metadata: remoteFeatureFlagControllerMetadata,
|
|
@@ -65,9 +67,11 @@ class RemoteFeatureFlagController extends base_controller_1.BaseController {
|
|
|
65
67
|
_RemoteFeatureFlagController_disabled.set(this, void 0);
|
|
66
68
|
_RemoteFeatureFlagController_clientConfigApiService.set(this, void 0);
|
|
67
69
|
_RemoteFeatureFlagController_inProgressFlagUpdate.set(this, void 0);
|
|
70
|
+
_RemoteFeatureFlagController_getMetaMetricsId.set(this, void 0);
|
|
68
71
|
__classPrivateFieldSet(this, _RemoteFeatureFlagController_fetchInterval, fetchInterval, "f");
|
|
69
72
|
__classPrivateFieldSet(this, _RemoteFeatureFlagController_disabled, disabled, "f");
|
|
70
73
|
__classPrivateFieldSet(this, _RemoteFeatureFlagController_clientConfigApiService, clientConfigApiService, "f");
|
|
74
|
+
__classPrivateFieldSet(this, _RemoteFeatureFlagController_getMetaMetricsId, getMetaMetricsId, "f");
|
|
71
75
|
}
|
|
72
76
|
/**
|
|
73
77
|
* Retrieves the remote feature flags, fetching from the API if necessary.
|
|
@@ -91,7 +95,7 @@ class RemoteFeatureFlagController extends base_controller_1.BaseController {
|
|
|
91
95
|
finally {
|
|
92
96
|
__classPrivateFieldSet(this, _RemoteFeatureFlagController_inProgressFlagUpdate, undefined, "f");
|
|
93
97
|
}
|
|
94
|
-
__classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_updateCache).call(this, serverData.remoteFeatureFlags);
|
|
98
|
+
await __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_updateCache).call(this, serverData.remoteFeatureFlags);
|
|
95
99
|
}
|
|
96
100
|
/**
|
|
97
101
|
* Enables the controller, allowing it to make network requests.
|
|
@@ -107,14 +111,45 @@ class RemoteFeatureFlagController extends base_controller_1.BaseController {
|
|
|
107
111
|
}
|
|
108
112
|
}
|
|
109
113
|
exports.RemoteFeatureFlagController = RemoteFeatureFlagController;
|
|
110
|
-
_RemoteFeatureFlagController_fetchInterval = new WeakMap(), _RemoteFeatureFlagController_disabled = new WeakMap(), _RemoteFeatureFlagController_clientConfigApiService = new WeakMap(), _RemoteFeatureFlagController_inProgressFlagUpdate = new WeakMap(), _RemoteFeatureFlagController_instances = new WeakSet(), _RemoteFeatureFlagController_isCacheExpired = function _RemoteFeatureFlagController_isCacheExpired() {
|
|
114
|
+
_RemoteFeatureFlagController_fetchInterval = new WeakMap(), _RemoteFeatureFlagController_disabled = new WeakMap(), _RemoteFeatureFlagController_clientConfigApiService = new WeakMap(), _RemoteFeatureFlagController_inProgressFlagUpdate = new WeakMap(), _RemoteFeatureFlagController_getMetaMetricsId = new WeakMap(), _RemoteFeatureFlagController_instances = new WeakSet(), _RemoteFeatureFlagController_isCacheExpired = function _RemoteFeatureFlagController_isCacheExpired() {
|
|
111
115
|
return Date.now() - this.state.cacheTimestamp > __classPrivateFieldGet(this, _RemoteFeatureFlagController_fetchInterval, "f");
|
|
112
|
-
}, _RemoteFeatureFlagController_updateCache =
|
|
116
|
+
}, _RemoteFeatureFlagController_updateCache =
|
|
117
|
+
/**
|
|
118
|
+
* Updates the controller's state with new feature flags and resets the cache timestamp.
|
|
119
|
+
*
|
|
120
|
+
* @param remoteFeatureFlags - The new feature flags to cache.
|
|
121
|
+
* @private
|
|
122
|
+
*/
|
|
123
|
+
async function _RemoteFeatureFlagController_updateCache(remoteFeatureFlags) {
|
|
124
|
+
const processedRemoteFeatureFlags = await __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_processRemoteFeatureFlags).call(this, remoteFeatureFlags);
|
|
113
125
|
this.update(() => {
|
|
114
126
|
return {
|
|
115
|
-
remoteFeatureFlags,
|
|
127
|
+
remoteFeatureFlags: processedRemoteFeatureFlags,
|
|
116
128
|
cacheTimestamp: Date.now(),
|
|
117
129
|
};
|
|
118
130
|
});
|
|
131
|
+
}, _RemoteFeatureFlagController_processRemoteFeatureFlags = async function _RemoteFeatureFlagController_processRemoteFeatureFlags(remoteFeatureFlags) {
|
|
132
|
+
const processedRemoteFeatureFlags = {};
|
|
133
|
+
const metaMetricsId = (await __classPrivateFieldGet(this, _RemoteFeatureFlagController_getMetaMetricsId, "f")) || (0, user_segmentation_utils_1.generateFallbackMetaMetricsId)();
|
|
134
|
+
const thresholdValue = (0, user_segmentation_utils_1.generateDeterministicRandomNumber)(metaMetricsId);
|
|
135
|
+
for (const [remoteFeatureFlagName, remoteFeatureFlagValue,] of Object.entries(remoteFeatureFlags)) {
|
|
136
|
+
let processedValue = remoteFeatureFlagValue;
|
|
137
|
+
if (Array.isArray(remoteFeatureFlagValue) && thresholdValue) {
|
|
138
|
+
const selectedGroup = remoteFeatureFlagValue.find((featureFlag) => {
|
|
139
|
+
if (!(0, user_segmentation_utils_1.isFeatureFlagWithScopeValue)(featureFlag)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return thresholdValue <= featureFlag.scope.value;
|
|
143
|
+
});
|
|
144
|
+
if (selectedGroup) {
|
|
145
|
+
processedValue = {
|
|
146
|
+
name: selectedGroup.name,
|
|
147
|
+
value: selectedGroup.value,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
processedRemoteFeatureFlags[remoteFeatureFlagName] = processedValue;
|
|
152
|
+
}
|
|
153
|
+
return processedRemoteFeatureFlags;
|
|
119
154
|
};
|
|
120
155
|
//# sourceMappingURL=remote-feature-flag-controller.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller.cjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAKA,+DAA2D;AAQ3D,kBAAkB;AAEL,QAAA,cAAc,GAAG,6BAA6B,CAAC;AAC/C,QAAA,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,QAAQ;AASnE,MAAM,mCAAmC,GAAG;IAC1C,kBAAkB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;IACtD,cAAc,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;CACnD,CAAC;AAwCF;;;;GAIG;AACH,SAAgB,0CAA0C;IACxD,OAAO;QACL,kBAAkB,EAAE,EAAE;QACtB,cAAc,EAAE,CAAC;KAClB,CAAC;AACJ,CAAC;AALD,gGAKC;AAED;;;;;GAKG;AACH,MAAa,2BAA4B,SAAQ,gCAIhD;IASC;;;;;;;;;OASG;IACH,YAAY,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAa,GAAG,8BAAsB,EACtC,QAAQ,GAAG,KAAK,GAOjB;QACC,KAAK,CAAC;YACJ,IAAI,EAAE,sBAAc;YACpB,QAAQ,EAAE,mCAAmC;YAC7C,SAAS;YACT,KAAK,EAAE;gBACL,GAAG,0CAA0C,EAAE;gBAC/C,GAAG,KAAK;aACT;SACF,CAAC,CAAC;;QAvCI,6DAAuB;QAEhC,wDAAmB;QAEnB,sEAAwD;QAExD,oEAAiD;QAmC/C,uBAAA,IAAI,8CAAkB,aAAa,MAAA,CAAC;QACpC,uBAAA,IAAI,yCAAa,QAAQ,MAAA,CAAC;QAC1B,uBAAA,IAAI,uDAA2B,sBAAsB,MAAA,CAAC;IACxD,CAAC;IAYD;;;;;OAKG;IACH,KAAK,CAAC,wBAAwB;QAC5B,IAAI,uBAAA,IAAI,6CAAU,IAAI,CAAC,uBAAA,IAAI,2FAAgB,MAApB,IAAI,CAAkB,EAAE;YAC7C,OAAO;SACR;QAED,IAAI,UAAU,CAAC;QAEf,IAAI,uBAAA,IAAI,yDAAsB,EAAE;YAC9B,MAAM,uBAAA,IAAI,yDAAsB,CAAC;YACjC,OAAO;SACR;QAED,IAAI;YACF,uBAAA,IAAI,qDACF,uBAAA,IAAI,2DAAwB,CAAC,uBAAuB,EAAE,MAAA,CAAC;YAEzD,UAAU,GAAG,MAAM,uBAAA,IAAI,yDAAsB,CAAC;SAC/C;gBAAS;YACR,uBAAA,IAAI,qDAAyB,SAAS,MAAA,CAAC;SACxC;QAED,uBAAA,IAAI,wFAAa,MAAjB,IAAI,EAAc,UAAU,CAAC,kBAAkB,CAAC,CAAC;IACnD,CAAC;IAiBD;;OAEG;IACH,MAAM;QACJ,uBAAA,IAAI,yCAAa,KAAK,MAAA,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,OAAO;QACL,uBAAA,IAAI,yCAAa,IAAI,MAAA,CAAC;IACxB,CAAC;CACF;AAvHD,kEAuHC;;IA7DG,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,uBAAA,IAAI,kDAAe,CAAC;AACtE,CAAC,+FAsCY,kBAAgC;IAC3C,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;QACf,OAAO;YACL,kBAAkB;YAClB,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;SAC3B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\n\nimport type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service';\nimport type {\n FeatureFlags,\n ServiceResponse,\n} from './remote-feature-flag-controller-types';\n\n// === GENERAL ===\n\nexport const controllerName = 'RemoteFeatureFlagController';\nexport const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day\n\n// === STATE ===\n\nexport type RemoteFeatureFlagControllerState = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: { persist: true, anonymous: true },\n cacheTimestamp: { persist: true, anonymous: true },\n};\n\n// === MESSENGER ===\n\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerGetRemoteFeatureFlagAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n RemoteFeatureFlagControllerGetStateAction;\n\nexport type AllowedActions = never;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type AllowedEvents = never;\n\nexport type RemoteFeatureFlagControllerMessenger =\n RestrictedControllerMessenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions | AllowedActions,\n RemoteFeatureFlagControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n >;\n\n/**\n * Returns the default state for the RemoteFeatureFlagController.\n *\n * @returns The default controller state.\n */\nexport function getDefaultRemoteFeatureFlagControllerState(): RemoteFeatureFlagControllerState {\n return {\n remoteFeatureFlags: {},\n cacheTimestamp: 0,\n };\n}\n\n/**\n * The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.\n * It fetches feature flags from a remote API, caches them, and provides methods to access\n * and manage these flags. The controller ensures that feature flags are refreshed based on\n * a specified interval and handles cases where the controller is disabled or the network is unavailable.\n */\nexport class RemoteFeatureFlagController extends BaseController<\n typeof controllerName,\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger\n> {\n readonly #fetchInterval: number;\n\n #disabled: boolean;\n\n #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The controller messenger used for communication.\n * @param options.state - The initial state of the controller.\n * @param options.clientConfigApiService - The service instance to fetch remote feature flags.\n * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.\n * @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n fetchInterval?: number;\n disabled?: boolean;\n }) {\n super({\n name: controllerName,\n metadata: remoteFeatureFlagControllerMetadata,\n messenger,\n state: {\n ...getDefaultRemoteFeatureFlagControllerState(),\n ...state,\n },\n });\n\n this.#fetchInterval = fetchInterval;\n this.#disabled = disabled;\n this.#clientConfigApiService = clientConfigApiService;\n }\n\n /**\n * Checks if the cached feature flags are expired based on the fetch interval.\n *\n * @returns Whether the cache is expired (`true`) or still valid (`false`).\n * @private\n */\n #isCacheExpired(): boolean {\n return Date.now() - this.state.cacheTimestamp > this.#fetchInterval;\n }\n\n /**\n * Retrieves the remote feature flags, fetching from the API if necessary.\n * Uses caching to prevent redundant API calls and handles concurrent fetches.\n *\n * @returns A promise that resolves to the current set of feature flags.\n */\n async updateRemoteFeatureFlags(): Promise<void> {\n if (this.#disabled || !this.#isCacheExpired()) {\n return;\n }\n\n let serverData;\n\n if (this.#inProgressFlagUpdate) {\n await this.#inProgressFlagUpdate;\n return;\n }\n\n try {\n this.#inProgressFlagUpdate =\n this.#clientConfigApiService.fetchRemoteFeatureFlags();\n\n serverData = await this.#inProgressFlagUpdate;\n } finally {\n this.#inProgressFlagUpdate = undefined;\n }\n\n this.#updateCache(serverData.remoteFeatureFlags);\n }\n\n /**\n * Updates the controller's state with new feature flags and resets the cache timestamp.\n *\n * @param remoteFeatureFlags - The new feature flags to cache.\n * @private\n */\n #updateCache(remoteFeatureFlags: FeatureFlags) {\n this.update(() => {\n return {\n remoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n /**\n * Enables the controller, allowing it to make network requests.\n */\n enable(): void {\n this.#disabled = false;\n }\n\n /**\n * Disables the controller, preventing it from making network requests.\n */\n disable(): void {\n this.#disabled = true;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remote-feature-flag-controller.cjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\n\nimport type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service';\nimport type {\n FeatureFlags,\n ServiceResponse,\n FeatureFlagScopeValue,\n} from './remote-feature-flag-controller-types';\nimport {\n generateDeterministicRandomNumber,\n isFeatureFlagWithScopeValue,\n generateFallbackMetaMetricsId,\n} from './utils/user-segmentation-utils';\n\n// === GENERAL ===\n\nexport const controllerName = 'RemoteFeatureFlagController';\nexport const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day\n\n// === STATE ===\n\nexport type RemoteFeatureFlagControllerState = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: { persist: true, anonymous: true },\n cacheTimestamp: { persist: true, anonymous: true },\n};\n\n// === MESSENGER ===\n\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerGetRemoteFeatureFlagAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n RemoteFeatureFlagControllerGetStateAction;\n\nexport type AllowedActions = never;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type AllowedEvents = never;\n\nexport type RemoteFeatureFlagControllerMessenger =\n RestrictedControllerMessenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions | AllowedActions,\n RemoteFeatureFlagControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n >;\n\n/**\n * Returns the default state for the RemoteFeatureFlagController.\n *\n * @returns The default controller state.\n */\nexport function getDefaultRemoteFeatureFlagControllerState(): RemoteFeatureFlagControllerState {\n return {\n remoteFeatureFlags: {},\n cacheTimestamp: 0,\n };\n}\n\n/**\n * The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.\n * It fetches feature flags from a remote API, caches them, and provides methods to access\n * and manage these flags. The controller ensures that feature flags are refreshed based on\n * a specified interval and handles cases where the controller is disabled or the network is unavailable.\n */\nexport class RemoteFeatureFlagController extends BaseController<\n typeof controllerName,\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger\n> {\n readonly #fetchInterval: number;\n\n #disabled: boolean;\n\n #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n #getMetaMetricsId?: Promise<string | undefined>;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The controller messenger used for communication.\n * @param options.state - The initial state of the controller.\n * @param options.clientConfigApiService - The service instance to fetch remote feature flags.\n * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.\n * @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.\n * @param options.getMetaMetricsId - Promise that resolves to a metaMetricsId.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId?: Promise<string | undefined>;\n fetchInterval?: number;\n disabled?: boolean;\n }) {\n super({\n name: controllerName,\n metadata: remoteFeatureFlagControllerMetadata,\n messenger,\n state: {\n ...getDefaultRemoteFeatureFlagControllerState(),\n ...state,\n },\n });\n\n this.#fetchInterval = fetchInterval;\n this.#disabled = disabled;\n this.#clientConfigApiService = clientConfigApiService;\n this.#getMetaMetricsId = getMetaMetricsId;\n }\n\n /**\n * Checks if the cached feature flags are expired based on the fetch interval.\n *\n * @returns Whether the cache is expired (`true`) or still valid (`false`).\n * @private\n */\n #isCacheExpired(): boolean {\n return Date.now() - this.state.cacheTimestamp > this.#fetchInterval;\n }\n\n /**\n * Retrieves the remote feature flags, fetching from the API if necessary.\n * Uses caching to prevent redundant API calls and handles concurrent fetches.\n *\n * @returns A promise that resolves to the current set of feature flags.\n */\n async updateRemoteFeatureFlags(): Promise<void> {\n if (this.#disabled || !this.#isCacheExpired()) {\n return;\n }\n\n let serverData;\n\n if (this.#inProgressFlagUpdate) {\n await this.#inProgressFlagUpdate;\n return;\n }\n\n try {\n this.#inProgressFlagUpdate =\n this.#clientConfigApiService.fetchRemoteFeatureFlags();\n\n serverData = await this.#inProgressFlagUpdate;\n } finally {\n this.#inProgressFlagUpdate = undefined;\n }\n\n await this.#updateCache(serverData.remoteFeatureFlags);\n }\n\n /**\n * Updates the controller's state with new feature flags and resets the cache timestamp.\n *\n * @param remoteFeatureFlags - The new feature flags to cache.\n * @private\n */\n async #updateCache(remoteFeatureFlags: FeatureFlags) {\n const processedRemoteFeatureFlags = await this.#processRemoteFeatureFlags(\n remoteFeatureFlags,\n );\n this.update(() => {\n return {\n remoteFeatureFlags: processedRemoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n async #processRemoteFeatureFlags(\n remoteFeatureFlags: FeatureFlags,\n ): Promise<FeatureFlags> {\n const processedRemoteFeatureFlags: FeatureFlags = {};\n const metaMetricsId =\n (await this.#getMetaMetricsId) || generateFallbackMetaMetricsId();\n const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = remoteFeatureFlagValue;\n\n if (Array.isArray(remoteFeatureFlagValue) && thresholdValue) {\n const selectedGroup = remoteFeatureFlagValue.find(\n (featureFlag): featureFlag is FeatureFlagScopeValue => {\n if (!isFeatureFlagWithScopeValue(featureFlag)) {\n return false;\n }\n\n return thresholdValue <= featureFlag.scope.value;\n },\n );\n if (selectedGroup) {\n processedValue = {\n name: selectedGroup.name,\n value: selectedGroup.value,\n };\n }\n }\n\n processedRemoteFeatureFlags[remoteFeatureFlagName] = processedValue;\n }\n return processedRemoteFeatureFlags;\n }\n\n /**\n * Enables the controller, allowing it to make network requests.\n */\n enable(): void {\n this.#disabled = false;\n }\n\n /**\n * Disables the controller, preventing it from making network requests.\n */\n disable(): void {\n this.#disabled = true;\n }\n}\n"]}
|
|
@@ -42,11 +42,13 @@ export declare class RemoteFeatureFlagController extends BaseController<typeof c
|
|
|
42
42
|
* @param options.clientConfigApiService - The service instance to fetch remote feature flags.
|
|
43
43
|
* @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.
|
|
44
44
|
* @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.
|
|
45
|
+
* @param options.getMetaMetricsId - Promise that resolves to a metaMetricsId.
|
|
45
46
|
*/
|
|
46
|
-
constructor({ messenger, state, clientConfigApiService, fetchInterval, disabled, }: {
|
|
47
|
+
constructor({ messenger, state, clientConfigApiService, fetchInterval, disabled, getMetaMetricsId, }: {
|
|
47
48
|
messenger: RemoteFeatureFlagControllerMessenger;
|
|
48
49
|
state?: Partial<RemoteFeatureFlagControllerState>;
|
|
49
50
|
clientConfigApiService: AbstractClientConfigApiService;
|
|
51
|
+
getMetaMetricsId?: Promise<string | undefined>;
|
|
50
52
|
fetchInterval?: number;
|
|
51
53
|
disabled?: boolean;
|
|
52
54
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller.d.cts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,6BAA6B,EAC9B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAAuE;AACrH,OAAO,KAAK,EACV,YAAY,
|
|
1
|
+
{"version":3,"file":"remote-feature-flag-controller.d.cts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,6BAA6B,EAC9B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAAuE;AACrH,OAAO,KAAK,EACV,YAAY,EAGb,mDAA+C;AAShD,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAC5D,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAI1D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AASF,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,qDAAqD,GAAG;IAClE,IAAI,EAAE,GAAG,OAAO,cAAc,2BAA2B,CAAC;IAC1D,OAAO,EAAE,2BAA2B,CAAC,0BAA0B,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAC5C,yCAAyC,CAAC;AAE5C,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC;AAEnC,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC;AAElC,MAAM,MAAM,oCAAoC,GAC9C,6BAA6B,CAC3B,OAAO,cAAc,EACrB,kCAAkC,GAAG,cAAc,EACnD,iCAAiC,GAAG,aAAa,EACjD,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEJ;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAK7F;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAWC;;;;;;;;;;OAUG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,GACjB,EAAE;QACD,SAAS,EAAE,oCAAoC,CAAC;QAChD,KAAK,CAAC,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAClD,sBAAsB,EAAE,8BAA8B,CAAC;QACvD,gBAAgB,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;QAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;IA2BD;;;;;OAKG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IA+E/C;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -42,11 +42,13 @@ export declare class RemoteFeatureFlagController extends BaseController<typeof c
|
|
|
42
42
|
* @param options.clientConfigApiService - The service instance to fetch remote feature flags.
|
|
43
43
|
* @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.
|
|
44
44
|
* @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.
|
|
45
|
+
* @param options.getMetaMetricsId - Promise that resolves to a metaMetricsId.
|
|
45
46
|
*/
|
|
46
|
-
constructor({ messenger, state, clientConfigApiService, fetchInterval, disabled, }: {
|
|
47
|
+
constructor({ messenger, state, clientConfigApiService, fetchInterval, disabled, getMetaMetricsId, }: {
|
|
47
48
|
messenger: RemoteFeatureFlagControllerMessenger;
|
|
48
49
|
state?: Partial<RemoteFeatureFlagControllerState>;
|
|
49
50
|
clientConfigApiService: AbstractClientConfigApiService;
|
|
51
|
+
getMetaMetricsId?: Promise<string | undefined>;
|
|
50
52
|
fetchInterval?: number;
|
|
51
53
|
disabled?: boolean;
|
|
52
54
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller.d.mts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,6BAA6B,EAC9B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAAuE;AACrH,OAAO,KAAK,EACV,YAAY,
|
|
1
|
+
{"version":3,"file":"remote-feature-flag-controller.d.mts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,6BAA6B,EAC9B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAAuE;AACrH,OAAO,KAAK,EACV,YAAY,EAGb,mDAA+C;AAShD,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAC5D,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAI1D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AASF,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,qDAAqD,GAAG;IAClE,IAAI,EAAE,GAAG,OAAO,cAAc,2BAA2B,CAAC;IAC1D,OAAO,EAAE,2BAA2B,CAAC,0BAA0B,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAC5C,yCAAyC,CAAC;AAE5C,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC;AAEnC,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC;AAElC,MAAM,MAAM,oCAAoC,GAC9C,6BAA6B,CAC3B,OAAO,cAAc,EACrB,kCAAkC,GAAG,cAAc,EACnD,iCAAiC,GAAG,aAAa,EACjD,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEJ;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAK7F;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAWC;;;;;;;;;;OAUG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,GACjB,EAAE;QACD,SAAS,EAAE,oCAAoC,CAAC;QAChD,KAAK,CAAC,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAClD,sBAAsB,EAAE,8BAA8B,CAAC;QACvD,gBAAgB,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;QAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;IA2BD;;;;;OAKG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IA+E/C;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -9,8 +9,9 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _RemoteFeatureFlagController_instances, _RemoteFeatureFlagController_fetchInterval, _RemoteFeatureFlagController_disabled, _RemoteFeatureFlagController_clientConfigApiService, _RemoteFeatureFlagController_inProgressFlagUpdate, _RemoteFeatureFlagController_isCacheExpired, _RemoteFeatureFlagController_updateCache;
|
|
12
|
+
var _RemoteFeatureFlagController_instances, _RemoteFeatureFlagController_fetchInterval, _RemoteFeatureFlagController_disabled, _RemoteFeatureFlagController_clientConfigApiService, _RemoteFeatureFlagController_inProgressFlagUpdate, _RemoteFeatureFlagController_getMetaMetricsId, _RemoteFeatureFlagController_isCacheExpired, _RemoteFeatureFlagController_updateCache, _RemoteFeatureFlagController_processRemoteFeatureFlags;
|
|
13
13
|
import { BaseController } from "@metamask/base-controller";
|
|
14
|
+
import { generateDeterministicRandomNumber, isFeatureFlagWithScopeValue, generateFallbackMetaMetricsId } from "./utils/user-segmentation-utils.mjs";
|
|
14
15
|
// === GENERAL ===
|
|
15
16
|
export const controllerName = 'RemoteFeatureFlagController';
|
|
16
17
|
export const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day
|
|
@@ -45,8 +46,9 @@ export class RemoteFeatureFlagController extends BaseController {
|
|
|
45
46
|
* @param options.clientConfigApiService - The service instance to fetch remote feature flags.
|
|
46
47
|
* @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.
|
|
47
48
|
* @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.
|
|
49
|
+
* @param options.getMetaMetricsId - Promise that resolves to a metaMetricsId.
|
|
48
50
|
*/
|
|
49
|
-
constructor({ messenger, state, clientConfigApiService, fetchInterval = DEFAULT_CACHE_DURATION, disabled = false, }) {
|
|
51
|
+
constructor({ messenger, state, clientConfigApiService, fetchInterval = DEFAULT_CACHE_DURATION, disabled = false, getMetaMetricsId, }) {
|
|
50
52
|
super({
|
|
51
53
|
name: controllerName,
|
|
52
54
|
metadata: remoteFeatureFlagControllerMetadata,
|
|
@@ -61,9 +63,11 @@ export class RemoteFeatureFlagController extends BaseController {
|
|
|
61
63
|
_RemoteFeatureFlagController_disabled.set(this, void 0);
|
|
62
64
|
_RemoteFeatureFlagController_clientConfigApiService.set(this, void 0);
|
|
63
65
|
_RemoteFeatureFlagController_inProgressFlagUpdate.set(this, void 0);
|
|
66
|
+
_RemoteFeatureFlagController_getMetaMetricsId.set(this, void 0);
|
|
64
67
|
__classPrivateFieldSet(this, _RemoteFeatureFlagController_fetchInterval, fetchInterval, "f");
|
|
65
68
|
__classPrivateFieldSet(this, _RemoteFeatureFlagController_disabled, disabled, "f");
|
|
66
69
|
__classPrivateFieldSet(this, _RemoteFeatureFlagController_clientConfigApiService, clientConfigApiService, "f");
|
|
70
|
+
__classPrivateFieldSet(this, _RemoteFeatureFlagController_getMetaMetricsId, getMetaMetricsId, "f");
|
|
67
71
|
}
|
|
68
72
|
/**
|
|
69
73
|
* Retrieves the remote feature flags, fetching from the API if necessary.
|
|
@@ -87,7 +91,7 @@ export class RemoteFeatureFlagController extends BaseController {
|
|
|
87
91
|
finally {
|
|
88
92
|
__classPrivateFieldSet(this, _RemoteFeatureFlagController_inProgressFlagUpdate, undefined, "f");
|
|
89
93
|
}
|
|
90
|
-
__classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_updateCache).call(this, serverData.remoteFeatureFlags);
|
|
94
|
+
await __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_updateCache).call(this, serverData.remoteFeatureFlags);
|
|
91
95
|
}
|
|
92
96
|
/**
|
|
93
97
|
* Enables the controller, allowing it to make network requests.
|
|
@@ -102,14 +106,45 @@ export class RemoteFeatureFlagController extends BaseController {
|
|
|
102
106
|
__classPrivateFieldSet(this, _RemoteFeatureFlagController_disabled, true, "f");
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
|
-
_RemoteFeatureFlagController_fetchInterval = new WeakMap(), _RemoteFeatureFlagController_disabled = new WeakMap(), _RemoteFeatureFlagController_clientConfigApiService = new WeakMap(), _RemoteFeatureFlagController_inProgressFlagUpdate = new WeakMap(), _RemoteFeatureFlagController_instances = new WeakSet(), _RemoteFeatureFlagController_isCacheExpired = function _RemoteFeatureFlagController_isCacheExpired() {
|
|
109
|
+
_RemoteFeatureFlagController_fetchInterval = new WeakMap(), _RemoteFeatureFlagController_disabled = new WeakMap(), _RemoteFeatureFlagController_clientConfigApiService = new WeakMap(), _RemoteFeatureFlagController_inProgressFlagUpdate = new WeakMap(), _RemoteFeatureFlagController_getMetaMetricsId = new WeakMap(), _RemoteFeatureFlagController_instances = new WeakSet(), _RemoteFeatureFlagController_isCacheExpired = function _RemoteFeatureFlagController_isCacheExpired() {
|
|
106
110
|
return Date.now() - this.state.cacheTimestamp > __classPrivateFieldGet(this, _RemoteFeatureFlagController_fetchInterval, "f");
|
|
107
|
-
}, _RemoteFeatureFlagController_updateCache =
|
|
111
|
+
}, _RemoteFeatureFlagController_updateCache =
|
|
112
|
+
/**
|
|
113
|
+
* Updates the controller's state with new feature flags and resets the cache timestamp.
|
|
114
|
+
*
|
|
115
|
+
* @param remoteFeatureFlags - The new feature flags to cache.
|
|
116
|
+
* @private
|
|
117
|
+
*/
|
|
118
|
+
async function _RemoteFeatureFlagController_updateCache(remoteFeatureFlags) {
|
|
119
|
+
const processedRemoteFeatureFlags = await __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_processRemoteFeatureFlags).call(this, remoteFeatureFlags);
|
|
108
120
|
this.update(() => {
|
|
109
121
|
return {
|
|
110
|
-
remoteFeatureFlags,
|
|
122
|
+
remoteFeatureFlags: processedRemoteFeatureFlags,
|
|
111
123
|
cacheTimestamp: Date.now(),
|
|
112
124
|
};
|
|
113
125
|
});
|
|
126
|
+
}, _RemoteFeatureFlagController_processRemoteFeatureFlags = async function _RemoteFeatureFlagController_processRemoteFeatureFlags(remoteFeatureFlags) {
|
|
127
|
+
const processedRemoteFeatureFlags = {};
|
|
128
|
+
const metaMetricsId = (await __classPrivateFieldGet(this, _RemoteFeatureFlagController_getMetaMetricsId, "f")) || generateFallbackMetaMetricsId();
|
|
129
|
+
const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);
|
|
130
|
+
for (const [remoteFeatureFlagName, remoteFeatureFlagValue,] of Object.entries(remoteFeatureFlags)) {
|
|
131
|
+
let processedValue = remoteFeatureFlagValue;
|
|
132
|
+
if (Array.isArray(remoteFeatureFlagValue) && thresholdValue) {
|
|
133
|
+
const selectedGroup = remoteFeatureFlagValue.find((featureFlag) => {
|
|
134
|
+
if (!isFeatureFlagWithScopeValue(featureFlag)) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
return thresholdValue <= featureFlag.scope.value;
|
|
138
|
+
});
|
|
139
|
+
if (selectedGroup) {
|
|
140
|
+
processedValue = {
|
|
141
|
+
name: selectedGroup.name,
|
|
142
|
+
value: selectedGroup.value,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
processedRemoteFeatureFlags[remoteFeatureFlagName] = processedValue;
|
|
147
|
+
}
|
|
148
|
+
return processedRemoteFeatureFlags;
|
|
114
149
|
};
|
|
115
150
|
//# sourceMappingURL=remote-feature-flag-controller.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller.mjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAKA,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAQ3D,kBAAkB;AAElB,MAAM,CAAC,MAAM,cAAc,GAAG,6BAA6B,CAAC;AAC5D,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,QAAQ;AASnE,MAAM,mCAAmC,GAAG;IAC1C,kBAAkB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;IACtD,cAAc,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;CACnD,CAAC;AAwCF;;;;GAIG;AACH,MAAM,UAAU,0CAA0C;IACxD,OAAO;QACL,kBAAkB,EAAE,EAAE;QACtB,cAAc,EAAE,CAAC;KAClB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,2BAA4B,SAAQ,cAIhD;IASC;;;;;;;;;OASG;IACH,YAAY,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAa,GAAG,sBAAsB,EACtC,QAAQ,GAAG,KAAK,GAOjB;QACC,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,mCAAmC;YAC7C,SAAS;YACT,KAAK,EAAE;gBACL,GAAG,0CAA0C,EAAE;gBAC/C,GAAG,KAAK;aACT;SACF,CAAC,CAAC;;QAvCI,6DAAuB;QAEhC,wDAAmB;QAEnB,sEAAwD;QAExD,oEAAiD;QAmC/C,uBAAA,IAAI,8CAAkB,aAAa,MAAA,CAAC;QACpC,uBAAA,IAAI,yCAAa,QAAQ,MAAA,CAAC;QAC1B,uBAAA,IAAI,uDAA2B,sBAAsB,MAAA,CAAC;IACxD,CAAC;IAYD;;;;;OAKG;IACH,KAAK,CAAC,wBAAwB;QAC5B,IAAI,uBAAA,IAAI,6CAAU,IAAI,CAAC,uBAAA,IAAI,2FAAgB,MAApB,IAAI,CAAkB,EAAE;YAC7C,OAAO;SACR;QAED,IAAI,UAAU,CAAC;QAEf,IAAI,uBAAA,IAAI,yDAAsB,EAAE;YAC9B,MAAM,uBAAA,IAAI,yDAAsB,CAAC;YACjC,OAAO;SACR;QAED,IAAI;YACF,uBAAA,IAAI,qDACF,uBAAA,IAAI,2DAAwB,CAAC,uBAAuB,EAAE,MAAA,CAAC;YAEzD,UAAU,GAAG,MAAM,uBAAA,IAAI,yDAAsB,CAAC;SAC/C;gBAAS;YACR,uBAAA,IAAI,qDAAyB,SAAS,MAAA,CAAC;SACxC;QAED,uBAAA,IAAI,wFAAa,MAAjB,IAAI,EAAc,UAAU,CAAC,kBAAkB,CAAC,CAAC;IACnD,CAAC;IAiBD;;OAEG;IACH,MAAM;QACJ,uBAAA,IAAI,yCAAa,KAAK,MAAA,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,OAAO;QACL,uBAAA,IAAI,yCAAa,IAAI,MAAA,CAAC;IACxB,CAAC;CACF;;IA7DG,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,uBAAA,IAAI,kDAAe,CAAC;AACtE,CAAC,+FAsCY,kBAAgC;IAC3C,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;QACf,OAAO;YACL,kBAAkB;YAClB,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;SAC3B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\n\nimport type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service';\nimport type {\n FeatureFlags,\n ServiceResponse,\n} from './remote-feature-flag-controller-types';\n\n// === GENERAL ===\n\nexport const controllerName = 'RemoteFeatureFlagController';\nexport const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day\n\n// === STATE ===\n\nexport type RemoteFeatureFlagControllerState = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: { persist: true, anonymous: true },\n cacheTimestamp: { persist: true, anonymous: true },\n};\n\n// === MESSENGER ===\n\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerGetRemoteFeatureFlagAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n RemoteFeatureFlagControllerGetStateAction;\n\nexport type AllowedActions = never;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type AllowedEvents = never;\n\nexport type RemoteFeatureFlagControllerMessenger =\n RestrictedControllerMessenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions | AllowedActions,\n RemoteFeatureFlagControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n >;\n\n/**\n * Returns the default state for the RemoteFeatureFlagController.\n *\n * @returns The default controller state.\n */\nexport function getDefaultRemoteFeatureFlagControllerState(): RemoteFeatureFlagControllerState {\n return {\n remoteFeatureFlags: {},\n cacheTimestamp: 0,\n };\n}\n\n/**\n * The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.\n * It fetches feature flags from a remote API, caches them, and provides methods to access\n * and manage these flags. The controller ensures that feature flags are refreshed based on\n * a specified interval and handles cases where the controller is disabled or the network is unavailable.\n */\nexport class RemoteFeatureFlagController extends BaseController<\n typeof controllerName,\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger\n> {\n readonly #fetchInterval: number;\n\n #disabled: boolean;\n\n #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The controller messenger used for communication.\n * @param options.state - The initial state of the controller.\n * @param options.clientConfigApiService - The service instance to fetch remote feature flags.\n * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.\n * @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n fetchInterval?: number;\n disabled?: boolean;\n }) {\n super({\n name: controllerName,\n metadata: remoteFeatureFlagControllerMetadata,\n messenger,\n state: {\n ...getDefaultRemoteFeatureFlagControllerState(),\n ...state,\n },\n });\n\n this.#fetchInterval = fetchInterval;\n this.#disabled = disabled;\n this.#clientConfigApiService = clientConfigApiService;\n }\n\n /**\n * Checks if the cached feature flags are expired based on the fetch interval.\n *\n * @returns Whether the cache is expired (`true`) or still valid (`false`).\n * @private\n */\n #isCacheExpired(): boolean {\n return Date.now() - this.state.cacheTimestamp > this.#fetchInterval;\n }\n\n /**\n * Retrieves the remote feature flags, fetching from the API if necessary.\n * Uses caching to prevent redundant API calls and handles concurrent fetches.\n *\n * @returns A promise that resolves to the current set of feature flags.\n */\n async updateRemoteFeatureFlags(): Promise<void> {\n if (this.#disabled || !this.#isCacheExpired()) {\n return;\n }\n\n let serverData;\n\n if (this.#inProgressFlagUpdate) {\n await this.#inProgressFlagUpdate;\n return;\n }\n\n try {\n this.#inProgressFlagUpdate =\n this.#clientConfigApiService.fetchRemoteFeatureFlags();\n\n serverData = await this.#inProgressFlagUpdate;\n } finally {\n this.#inProgressFlagUpdate = undefined;\n }\n\n this.#updateCache(serverData.remoteFeatureFlags);\n }\n\n /**\n * Updates the controller's state with new feature flags and resets the cache timestamp.\n *\n * @param remoteFeatureFlags - The new feature flags to cache.\n * @private\n */\n #updateCache(remoteFeatureFlags: FeatureFlags) {\n this.update(() => {\n return {\n remoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n /**\n * Enables the controller, allowing it to make network requests.\n */\n enable(): void {\n this.#disabled = false;\n }\n\n /**\n * Disables the controller, preventing it from making network requests.\n */\n disable(): void {\n this.#disabled = true;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remote-feature-flag-controller.mjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\n\nimport type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service';\nimport type {\n FeatureFlags,\n ServiceResponse,\n FeatureFlagScopeValue,\n} from './remote-feature-flag-controller-types';\nimport {\n generateDeterministicRandomNumber,\n isFeatureFlagWithScopeValue,\n generateFallbackMetaMetricsId,\n} from './utils/user-segmentation-utils';\n\n// === GENERAL ===\n\nexport const controllerName = 'RemoteFeatureFlagController';\nexport const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day\n\n// === STATE ===\n\nexport type RemoteFeatureFlagControllerState = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: { persist: true, anonymous: true },\n cacheTimestamp: { persist: true, anonymous: true },\n};\n\n// === MESSENGER ===\n\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerGetRemoteFeatureFlagAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n RemoteFeatureFlagControllerGetStateAction;\n\nexport type AllowedActions = never;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type AllowedEvents = never;\n\nexport type RemoteFeatureFlagControllerMessenger =\n RestrictedControllerMessenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions | AllowedActions,\n RemoteFeatureFlagControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n >;\n\n/**\n * Returns the default state for the RemoteFeatureFlagController.\n *\n * @returns The default controller state.\n */\nexport function getDefaultRemoteFeatureFlagControllerState(): RemoteFeatureFlagControllerState {\n return {\n remoteFeatureFlags: {},\n cacheTimestamp: 0,\n };\n}\n\n/**\n * The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.\n * It fetches feature flags from a remote API, caches them, and provides methods to access\n * and manage these flags. The controller ensures that feature flags are refreshed based on\n * a specified interval and handles cases where the controller is disabled or the network is unavailable.\n */\nexport class RemoteFeatureFlagController extends BaseController<\n typeof controllerName,\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger\n> {\n readonly #fetchInterval: number;\n\n #disabled: boolean;\n\n #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n #getMetaMetricsId?: Promise<string | undefined>;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The controller messenger used for communication.\n * @param options.state - The initial state of the controller.\n * @param options.clientConfigApiService - The service instance to fetch remote feature flags.\n * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.\n * @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.\n * @param options.getMetaMetricsId - Promise that resolves to a metaMetricsId.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId?: Promise<string | undefined>;\n fetchInterval?: number;\n disabled?: boolean;\n }) {\n super({\n name: controllerName,\n metadata: remoteFeatureFlagControllerMetadata,\n messenger,\n state: {\n ...getDefaultRemoteFeatureFlagControllerState(),\n ...state,\n },\n });\n\n this.#fetchInterval = fetchInterval;\n this.#disabled = disabled;\n this.#clientConfigApiService = clientConfigApiService;\n this.#getMetaMetricsId = getMetaMetricsId;\n }\n\n /**\n * Checks if the cached feature flags are expired based on the fetch interval.\n *\n * @returns Whether the cache is expired (`true`) or still valid (`false`).\n * @private\n */\n #isCacheExpired(): boolean {\n return Date.now() - this.state.cacheTimestamp > this.#fetchInterval;\n }\n\n /**\n * Retrieves the remote feature flags, fetching from the API if necessary.\n * Uses caching to prevent redundant API calls and handles concurrent fetches.\n *\n * @returns A promise that resolves to the current set of feature flags.\n */\n async updateRemoteFeatureFlags(): Promise<void> {\n if (this.#disabled || !this.#isCacheExpired()) {\n return;\n }\n\n let serverData;\n\n if (this.#inProgressFlagUpdate) {\n await this.#inProgressFlagUpdate;\n return;\n }\n\n try {\n this.#inProgressFlagUpdate =\n this.#clientConfigApiService.fetchRemoteFeatureFlags();\n\n serverData = await this.#inProgressFlagUpdate;\n } finally {\n this.#inProgressFlagUpdate = undefined;\n }\n\n await this.#updateCache(serverData.remoteFeatureFlags);\n }\n\n /**\n * Updates the controller's state with new feature flags and resets the cache timestamp.\n *\n * @param remoteFeatureFlags - The new feature flags to cache.\n * @private\n */\n async #updateCache(remoteFeatureFlags: FeatureFlags) {\n const processedRemoteFeatureFlags = await this.#processRemoteFeatureFlags(\n remoteFeatureFlags,\n );\n this.update(() => {\n return {\n remoteFeatureFlags: processedRemoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n async #processRemoteFeatureFlags(\n remoteFeatureFlags: FeatureFlags,\n ): Promise<FeatureFlags> {\n const processedRemoteFeatureFlags: FeatureFlags = {};\n const metaMetricsId =\n (await this.#getMetaMetricsId) || generateFallbackMetaMetricsId();\n const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = remoteFeatureFlagValue;\n\n if (Array.isArray(remoteFeatureFlagValue) && thresholdValue) {\n const selectedGroup = remoteFeatureFlagValue.find(\n (featureFlag): featureFlag is FeatureFlagScopeValue => {\n if (!isFeatureFlagWithScopeValue(featureFlag)) {\n return false;\n }\n\n return thresholdValue <= featureFlag.scope.value;\n },\n );\n if (selectedGroup) {\n processedValue = {\n name: selectedGroup.name,\n value: selectedGroup.value,\n };\n }\n }\n\n processedRemoteFeatureFlags[remoteFeatureFlagName] = processedValue;\n }\n return processedRemoteFeatureFlags;\n }\n\n /**\n * Enables the controller, allowing it to make network requests.\n */\n enable(): void {\n this.#disabled = false;\n }\n\n /**\n * Disables the controller, preventing it from making network requests.\n */\n disable(): void {\n this.#disabled = true;\n }\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateFallbackMetaMetricsId = exports.isFeatureFlagWithScopeValue = exports.generateDeterministicRandomNumber = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
/* eslint-disable no-bitwise */
|
|
6
|
+
/**
|
|
7
|
+
* Generates a deterministic random number between 0 and 1 based on a metaMetricsId.
|
|
8
|
+
* This is useful for A/B testing and feature flag rollouts where we want
|
|
9
|
+
* consistent group assignment for the same user.
|
|
10
|
+
*
|
|
11
|
+
* @param metaMetricsId - The unique identifier used to generate the deterministic random number
|
|
12
|
+
* @returns A number between 0 and 1 that is deterministic for the given metaMetricsId
|
|
13
|
+
*/
|
|
14
|
+
function generateDeterministicRandomNumber(metaMetricsId) {
|
|
15
|
+
const hash = [...metaMetricsId].reduce((acc, char) => {
|
|
16
|
+
const chr = char.charCodeAt(0);
|
|
17
|
+
return ((acc << 5) - acc + chr) | 0;
|
|
18
|
+
}, 0);
|
|
19
|
+
return (hash >>> 0) / 0xffffffff;
|
|
20
|
+
}
|
|
21
|
+
exports.generateDeterministicRandomNumber = generateDeterministicRandomNumber;
|
|
22
|
+
/**
|
|
23
|
+
* Type guard to check if a value is a feature flag with scope.
|
|
24
|
+
* Used to validate feature flag objects that contain scope-based configurations.
|
|
25
|
+
*
|
|
26
|
+
* @param featureFlag - The value to check if it's a feature flag with scope
|
|
27
|
+
* @returns True if the value is a feature flag with scope, false otherwise
|
|
28
|
+
*/
|
|
29
|
+
const isFeatureFlagWithScopeValue = (featureFlag) => {
|
|
30
|
+
return (typeof featureFlag === 'object' &&
|
|
31
|
+
featureFlag !== null &&
|
|
32
|
+
'scope' in featureFlag);
|
|
33
|
+
};
|
|
34
|
+
exports.isFeatureFlagWithScopeValue = isFeatureFlagWithScopeValue;
|
|
35
|
+
/**
|
|
36
|
+
* Generates UUIDv4 as a fallback metaMetricsId
|
|
37
|
+
* @returns A UUIDv4 string
|
|
38
|
+
*/
|
|
39
|
+
function generateFallbackMetaMetricsId() {
|
|
40
|
+
return (0, uuid_1.v4)();
|
|
41
|
+
}
|
|
42
|
+
exports.generateFallbackMetaMetricsId = generateFallbackMetaMetricsId;
|
|
43
|
+
//# sourceMappingURL=user-segmentation-utils.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-segmentation-utils.cjs","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":";;;AACA,+BAAoC;AAIpC,+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;AAEF;;;GAGG;AACH,SAAgB,6BAA6B;IAC3C,OAAO,IAAA,SAAM,GAAE,CAAC;AAClB,CAAC;AAFD,sEAEC","sourcesContent":["import type { Json } from '@metamask/utils';\nimport { v4 as uuidV4 } from 'uuid';\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\n/**\n * Generates UUIDv4 as a fallback metaMetricsId\n * @returns A UUIDv4 string\n */\nexport function generateFallbackMetaMetricsId(): string {\n return uuidV4();\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Json } from "@metamask/utils";
|
|
2
|
+
import type { FeatureFlagScopeValue } from "../remote-feature-flag-controller-types.cjs";
|
|
3
|
+
/**
|
|
4
|
+
* Generates a deterministic random number between 0 and 1 based on a metaMetricsId.
|
|
5
|
+
* This is useful for A/B testing and feature flag rollouts where we want
|
|
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
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateDeterministicRandomNumber(metaMetricsId: string): number;
|
|
12
|
+
/**
|
|
13
|
+
* Type guard to check if a value is a feature flag with scope.
|
|
14
|
+
* Used to validate feature flag objects that contain scope-based configurations.
|
|
15
|
+
*
|
|
16
|
+
* @param featureFlag - The value to check if it's a feature flag with scope
|
|
17
|
+
* @returns True if the value is a feature flag with scope, false otherwise
|
|
18
|
+
*/
|
|
19
|
+
export declare const isFeatureFlagWithScopeValue: (featureFlag: Json) => featureFlag is FeatureFlagScopeValue;
|
|
20
|
+
/**
|
|
21
|
+
* Generates UUIDv4 as a fallback metaMetricsId
|
|
22
|
+
* @returns A UUIDv4 string
|
|
23
|
+
*/
|
|
24
|
+
export declare function generateFallbackMetaMetricsId(): string;
|
|
25
|
+
//# sourceMappingURL=user-segmentation-utils.d.cts.map
|
|
@@ -0,0 +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;AAG5C,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;AAEF;;;GAGG;AACH,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Json } from "@metamask/utils";
|
|
2
|
+
import type { FeatureFlagScopeValue } from "../remote-feature-flag-controller-types.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Generates a deterministic random number between 0 and 1 based on a metaMetricsId.
|
|
5
|
+
* This is useful for A/B testing and feature flag rollouts where we want
|
|
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
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateDeterministicRandomNumber(metaMetricsId: string): number;
|
|
12
|
+
/**
|
|
13
|
+
* Type guard to check if a value is a feature flag with scope.
|
|
14
|
+
* Used to validate feature flag objects that contain scope-based configurations.
|
|
15
|
+
*
|
|
16
|
+
* @param featureFlag - The value to check if it's a feature flag with scope
|
|
17
|
+
* @returns True if the value is a feature flag with scope, false otherwise
|
|
18
|
+
*/
|
|
19
|
+
export declare const isFeatureFlagWithScopeValue: (featureFlag: Json) => featureFlag is FeatureFlagScopeValue;
|
|
20
|
+
/**
|
|
21
|
+
* Generates UUIDv4 as a fallback metaMetricsId
|
|
22
|
+
* @returns A UUIDv4 string
|
|
23
|
+
*/
|
|
24
|
+
export declare function generateFallbackMetaMetricsId(): string;
|
|
25
|
+
//# sourceMappingURL=user-segmentation-utils.d.mts.map
|
|
@@ -0,0 +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;AAG5C,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;AAEF;;;GAGG;AACH,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { v4 as uuidV4 } from "uuid";
|
|
2
|
+
/* eslint-disable no-bitwise */
|
|
3
|
+
/**
|
|
4
|
+
* Generates a deterministic random number between 0 and 1 based on a metaMetricsId.
|
|
5
|
+
* This is useful for A/B testing and feature flag rollouts where we want
|
|
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
|
|
10
|
+
*/
|
|
11
|
+
export function generateDeterministicRandomNumber(metaMetricsId) {
|
|
12
|
+
const hash = [...metaMetricsId].reduce((acc, char) => {
|
|
13
|
+
const chr = char.charCodeAt(0);
|
|
14
|
+
return ((acc << 5) - acc + chr) | 0;
|
|
15
|
+
}, 0);
|
|
16
|
+
return (hash >>> 0) / 0xffffffff;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Type guard to check if a value is a feature flag with scope.
|
|
20
|
+
* Used to validate feature flag objects that contain scope-based configurations.
|
|
21
|
+
*
|
|
22
|
+
* @param featureFlag - The value to check if it's a feature flag with scope
|
|
23
|
+
* @returns True if the value is a feature flag with scope, false otherwise
|
|
24
|
+
*/
|
|
25
|
+
export const isFeatureFlagWithScopeValue = (featureFlag) => {
|
|
26
|
+
return (typeof featureFlag === 'object' &&
|
|
27
|
+
featureFlag !== null &&
|
|
28
|
+
'scope' in featureFlag);
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Generates UUIDv4 as a fallback metaMetricsId
|
|
32
|
+
* @returns A UUIDv4 string
|
|
33
|
+
*/
|
|
34
|
+
export function generateFallbackMetaMetricsId() {
|
|
35
|
+
return uuidV4();
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=user-segmentation-utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-segmentation-utils.mjs","sourceRoot":"","sources":["../../src/utils/user-segmentation-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,aAAa;AAIpC,+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;AAEF;;;GAGG;AACH,MAAM,UAAU,6BAA6B;IAC3C,OAAO,MAAM,EAAE,CAAC;AAClB,CAAC","sourcesContent":["import type { Json } from '@metamask/utils';\nimport { v4 as uuidV4 } from 'uuid';\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\n/**\n * Generates UUIDv4 as a fallback metaMetricsId\n * @returns A UUIDv4 string\n */\nexport function generateFallbackMetaMetricsId(): string {\n return uuidV4();\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.2.0-preview-cdf3e01b",
|
|
4
4
|
"description": "The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@metamask/base-controller": "^7.0.2",
|
|
51
51
|
"@metamask/utils": "^10.0.0",
|
|
52
|
-
"cockatiel": "^3.1.2"
|
|
52
|
+
"cockatiel": "^3.1.2",
|
|
53
|
+
"uuid": "^8.3.2"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
56
|
"@lavamoat/allow-scripts": "^3.0.4",
|