You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@metamask/remote-feature-flag-controller

Package Overview
Dependencies
Maintainers
3
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@metamask/remote-feature-flag-controller - npm Package Compare versions

Comparing version
3.0.0
to
3.1.0
+18
-1
CHANGELOG.md

@@ -10,2 +10,18 @@ # Changelog

## [3.1.0]
### Added
- Add override functionality to remote feature flags ([#7271](https://github.com/MetaMask/core/pull/7271))
- `setFlagOverride(flagName, value)` - Set a local override for a specific feature flag
- `removeFlagOverride(flagName)` - Clear the local override for a specific feature flag
- `clearAllFlagOverrides()` - Clear all local feature flag overrides
- Add new optional controller state properties ([#7271](https://github.com/MetaMask/core/pull/7271))
- `localOverrides` - Local overrides for feature flags that take precedence over remote flags
- `rawRemoteFeatureFlags` - Raw flag value for all feature flags
- Export additional controller action types ([#7271](https://github.com/MetaMask/core/pull/7271))
- `RemoteFeatureFlagControllerSetFlagOverrideAction`
- `RemoteFeatureFlagControllerremoveFlagOverrideAction`
- `RemoteFeatureFlagControllerclearAllFlagOverridesAction`
## [3.0.0]

@@ -151,3 +167,4 @@

[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@3.0.0...HEAD
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@3.1.0...HEAD
[3.1.0]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@3.0.0...@metamask/remote-feature-flag-controller@3.1.0
[3.0.0]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@2.0.1...@metamask/remote-feature-flag-controller@3.0.0

@@ -154,0 +171,0 @@ [2.0.1]: https://github.com/MetaMask/core/compare/@metamask/remote-feature-flag-controller@2.0.0...@metamask/remote-feature-flag-controller@2.0.1

+8
-17

@@ -13,3 +13,3 @@ "use strict";

};
var _ClientConfigApiService_fetch, _ClientConfigApiService_policy, _ClientConfigApiService_client, _ClientConfigApiService_distribution, _ClientConfigApiService_environment;
var _ClientConfigApiService_instances, _ClientConfigApiService_fetch, _ClientConfigApiService_policy, _ClientConfigApiService_client, _ClientConfigApiService_distribution, _ClientConfigApiService_environment, _ClientConfigApiService_flattenFeatureFlags;
Object.defineProperty(exports, "__esModule", { value: true });

@@ -24,2 +24,3 @@ exports.ClientConfigApiService = void 0;

constructor({ fetch: fetchFunction, retries = controller_utils_1.DEFAULT_MAX_RETRIES, maximumConsecutiveFailures = controller_utils_1.DEFAULT_MAX_CONSECUTIVE_FAILURES, circuitBreakDuration = controller_utils_1.DEFAULT_CIRCUIT_BREAK_DURATION, onBreak, onDegraded, config, }) {
_ClientConfigApiService_instances.add(this);
_ClientConfigApiService_fetch.set(this, void 0);

@@ -82,3 +83,3 @@ _ClientConfigApiService_policy.set(this, void 0);

}
const remoteFeatureFlags = this.flattenFeatureFlags(data);
const remoteFeatureFlags = __classPrivateFieldGet(this, _ClientConfigApiService_instances, "m", _ClientConfigApiService_flattenFeatureFlags).call(this, data);
return {

@@ -89,19 +90,9 @@ remoteFeatureFlags,

}
/**
* Flattens an array of feature flag objects into a single feature flags object.
*
* @param responseData - Array of objects containing feature flag key-value pairs
* @returns A single object containing all feature flags merged together
* @example
* // Input: [{ flag1: true }, { flag2: [] }]
* // Output: { flag1: true, flag2: [] }
*/
flattenFeatureFlags(responseData) {
return responseData.reduce((acc, curr) => {
return { ...acc, ...curr };
}, {});
}
}
exports.ClientConfigApiService = ClientConfigApiService;
_ClientConfigApiService_fetch = new WeakMap(), _ClientConfigApiService_policy = new WeakMap(), _ClientConfigApiService_client = new WeakMap(), _ClientConfigApiService_distribution = new WeakMap(), _ClientConfigApiService_environment = new WeakMap();
_ClientConfigApiService_fetch = new WeakMap(), _ClientConfigApiService_policy = new WeakMap(), _ClientConfigApiService_client = new WeakMap(), _ClientConfigApiService_distribution = new WeakMap(), _ClientConfigApiService_environment = new WeakMap(), _ClientConfigApiService_instances = new WeakSet(), _ClientConfigApiService_flattenFeatureFlags = function _ClientConfigApiService_flattenFeatureFlags(responseData) {
return responseData.reduce((acc, curr) => {
return { ...acc, ...curr };
}, {});
};
//# sourceMappingURL=client-config-api-service.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"client-config-api-service.cjs","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,iEAKoC;AAIpC,gDAAwC;AAUxC;;GAEG;AACH,MAAa,sBAAsB;IAwFjC,YAAY,EACV,KAAK,EAAE,aAAa,EACpB,OAAO,GAAG,sCAAmB,EAC7B,0BAA0B,GAAG,mDAAgC,EAC7D,oBAAoB,GAAG,iDAA8B,EACrD,OAAO,EACP,UAAU,EACV,MAAM,GAaP;QA3GQ,gDAAqB;QAErB,iDAAuB;QAEvB,iDAAoB;QAEpB,uDAAgC;QAEhC,sDAA8B;QAoGrC,uBAAA,IAAI,iCAAU,aAAa,MAAA,CAAC;QAC5B,uBAAA,IAAI,kCAAW,MAAM,CAAC,MAAM,MAAA,CAAC;QAC7B,uBAAA,IAAI,wCAAiB,MAAM,CAAC,YAAY,MAAA,CAAC;QACzC,uBAAA,IAAI,uCAAgB,MAAM,CAAC,WAAW,MAAA,CAAC;QAEvC,uBAAA,IAAI,kCAAW,IAAA,sCAAmB,EAAC;YACjC,UAAU,EAAE,OAAO;YACnB,sBAAsB,EAAE,0BAA0B;YAClD,oBAAoB;SACrB,CAAC,MAAA,CAAC;QACH,IAAI,OAAO,EAAE,CAAC;YACZ,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,UAAU,EAAE,CAAC;YACf,uBAAA,IAAI,sCAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,IAA0C;QACnD,OAAO,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,IAA6C;QACzD,OAAO,uBAAA,IAAI,sCAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,uBAAuB;QAClC,MAAM,GAAG,GAAG,GAAG,oBAAQ,iBAAiB,uBAAA,IAAI,sCAAQ,iBAClD,uBAAA,IAAI,4CACN,gBAAgB,uBAAA,IAAI,2CAAa,EAAE,CAAC;QAEpC,MAAM,QAAQ,GAAG,MAAM,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAC/C,uBAAA,IAAI,qCAAO,MAAX,IAAI,EAAQ,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CACxC,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,kBAAkB,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAE1D,OAAO;YACL,kBAAkB;YAClB,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;SAC3B,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACK,mBAAmB,CAAC,YAA6B;QACvD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;YACvC,OAAO,EAAE,GAAG,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;QAC7B,CAAC,EAAE,EAAE,CAAC,CAAC;IACT,CAAC;CACF;AApMD,wDAoMC","sourcesContent":["import {\n createServicePolicy,\n DEFAULT_CIRCUIT_BREAK_DURATION,\n DEFAULT_MAX_CONSECUTIVE_FAILURES,\n DEFAULT_MAX_RETRIES,\n} from '@metamask/controller-utils';\nimport type { ServicePolicy } from '@metamask/controller-utils';\n\nimport type { AbstractClientConfigApiService } from './abstract-client-config-api-service';\nimport { BASE_URL } from '../constants';\nimport type {\n FeatureFlags,\n ClientType,\n DistributionType,\n EnvironmentType,\n ServiceResponse,\n ApiDataResponse,\n} from '../remote-feature-flag-controller-types';\n\n/**\n * This service is responsible for fetching feature flags from the ClientConfig API.\n */\nexport class ClientConfigApiService implements AbstractClientConfigApiService {\n readonly #fetch: typeof fetch;\n\n readonly #policy: ServicePolicy;\n\n readonly #client: ClientType;\n\n readonly #distribution: DistributionType;\n\n readonly #environment: EnvironmentType;\n\n /**\n * Constructs a new ClientConfigApiService object.\n *\n * @param args - The arguments.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * If your JavaScript environment supports `fetch` natively, you'll probably\n * want to pass that; otherwise you can pass an equivalent (such as `fetch`\n * via `node-fetch`).\n * @param args.retries - Number of retry attempts for each fetch request.\n * @param args.maximumConsecutiveFailures - The maximum number of consecutive\n * failures allowed before breaking the circuit and pausing further fetch\n * attempts.\n * @param args.circuitBreakDuration - The amount of time to wait when the\n * circuit breaks from too many consecutive failures.\n * @param args.config - The configuration object, includes client,\n * distribution, and environment.\n * @param args.config.client - The client type (e.g., 'extension', 'mobile').\n * @param args.config.distribution - The distribution type (e.g., 'main',\n * 'flask').\n * @param args.config.environment - The environment type (e.g., 'prod', 'rc',\n * 'dev').\n */\n constructor(args: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n });\n\n /**\n * Constructs a new ClientConfigApiService object.\n *\n * @deprecated This signature is deprecated; please use the `onBreak` and\n * `onDegraded` methods instead.\n * @param args - The arguments.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * If your JavaScript environment supports `fetch` natively, you'll probably\n * want to pass that; otherwise you can pass an equivalent (such as `fetch`\n * via `node-fetch`).\n * @param args.retries - Number of retry attempts for each fetch request.\n * @param args.maximumConsecutiveFailures - The maximum number of consecutive\n * failures allowed before breaking the circuit and pausing further fetch\n * attempts.\n * @param args.circuitBreakDuration - The amount of time to wait when the\n * circuit breaks from too many consecutive failures.\n * @param args.onBreak - Callback for when the circuit breaks, useful\n * for capturing metrics about network failures.\n * @param args.onDegraded - Callback for when the API responds successfully\n * but takes too long to respond (5 seconds or more).\n * @param args.config - The configuration object, includes client,\n * distribution, and environment.\n * @param args.config.client - The client type (e.g., 'extension', 'mobile').\n * @param args.config.distribution - The distribution type (e.g., 'main',\n * 'flask').\n * @param args.config.environment - The environment type (e.g., 'prod', 'rc',\n * 'dev').\n */\n // eslint-disable-next-line @typescript-eslint/unified-signatures\n constructor(args: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n onBreak?: () => void;\n onDegraded?: () => void;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n });\n\n constructor({\n fetch: fetchFunction,\n retries = DEFAULT_MAX_RETRIES,\n maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES,\n circuitBreakDuration = DEFAULT_CIRCUIT_BREAK_DURATION,\n onBreak,\n onDegraded,\n config,\n }: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n onBreak?: () => void;\n onDegraded?: () => void;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n }) {\n this.#fetch = fetchFunction;\n this.#client = config.client;\n this.#distribution = config.distribution;\n this.#environment = config.environment;\n\n this.#policy = createServicePolicy({\n maxRetries: retries,\n maxConsecutiveFailures: maximumConsecutiveFailures,\n circuitBreakDuration,\n });\n if (onBreak) {\n this.#policy.onBreak(onBreak);\n }\n if (onDegraded) {\n this.#policy.onDegraded(onDegraded);\n }\n }\n\n /**\n * Listens for when the request to the API fails too many times in a row.\n *\n * @param args - The same arguments that {@link ServicePolicy.onBreak}\n * takes.\n * @returns What {@link ServicePolicy.onBreak} returns.\n */\n onBreak(...args: Parameters<ServicePolicy['onBreak']>) {\n return this.#policy.onBreak(...args);\n }\n\n /**\n * Listens for when the API is degraded.\n *\n * @param args - The same arguments that {@link ServicePolicy.onDegraded}\n * takes.\n * @returns What {@link ServicePolicy.onDegraded} returns.\n */\n onDegraded(...args: Parameters<ServicePolicy['onDegraded']>) {\n return this.#policy.onDegraded(...args);\n }\n\n /**\n * Fetches feature flags from the API with specific client, distribution, and environment parameters.\n * Provides structured error handling, including fallback to cached data if available.\n *\n * @returns An object of feature flags and their boolean values or a structured error object.\n */\n public async fetchRemoteFeatureFlags(): Promise<ServiceResponse> {\n const url = `${BASE_URL}/flags?client=${this.#client}&distribution=${\n this.#distribution\n }&environment=${this.#environment}`;\n\n const response = await this.#policy.execute(() =>\n this.#fetch(url, { cache: 'no-cache' }),\n );\n\n if (!response.ok) {\n throw new Error('Failed to fetch remote feature flags');\n }\n\n const data = await response.json();\n\n if (!Array.isArray(data)) {\n throw new Error('Feature flags api did not return an array');\n }\n\n const remoteFeatureFlags = this.flattenFeatureFlags(data);\n\n return {\n remoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n }\n\n /**\n * Flattens an array of feature flag objects into a single feature flags object.\n *\n * @param responseData - Array of objects containing feature flag key-value pairs\n * @returns A single object containing all feature flags merged together\n * @example\n * // Input: [{ flag1: true }, { flag2: [] }]\n * // Output: { flag1: true, flag2: [] }\n */\n private flattenFeatureFlags(responseData: ApiDataResponse): FeatureFlags {\n return responseData.reduce((acc, curr) => {\n return { ...acc, ...curr };\n }, {});\n }\n}\n"]}
{"version":3,"file":"client-config-api-service.cjs","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,iEAKoC;AAKpC,gDAAwC;AAUxC;;GAEG;AACH,MAAa,sBAAsB;IAwFjC,YAAY,EACV,KAAK,EAAE,aAAa,EACpB,OAAO,GAAG,sCAAmB,EAC7B,0BAA0B,GAAG,mDAAgC,EAC7D,oBAAoB,GAAG,iDAA8B,EACrD,OAAO,EACP,UAAU,EACV,MAAM,GAaP;;QA3GQ,gDAAqB;QAErB,iDAAuB;QAEvB,iDAAoB;QAEpB,uDAAgC;QAEhC,sDAA8B;QAoGrC,uBAAA,IAAI,iCAAU,aAAa,MAAA,CAAC;QAC5B,uBAAA,IAAI,kCAAW,MAAM,CAAC,MAAM,MAAA,CAAC;QAC7B,uBAAA,IAAI,wCAAiB,MAAM,CAAC,YAAY,MAAA,CAAC;QACzC,uBAAA,IAAI,uCAAgB,MAAM,CAAC,WAAW,MAAA,CAAC;QAEvC,uBAAA,IAAI,kCAAW,IAAA,sCAAmB,EAAC;YACjC,UAAU,EAAE,OAAO;YACnB,sBAAsB,EAAE,0BAA0B;YAClD,oBAAoB;SACrB,CAAC,MAAA,CAAC;QACH,IAAI,OAAO,EAAE,CAAC;YACZ,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,UAAU,EAAE,CAAC;YACf,uBAAA,IAAI,sCAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,IAA0C;QACnD,OAAO,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,IAA6C;QACzD,OAAO,uBAAA,IAAI,sCAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,uBAAuB;QAClC,MAAM,GAAG,GAAG,GAAG,oBAAQ,iBAAiB,uBAAA,IAAI,sCAAQ,iBAClD,uBAAA,IAAI,4CACN,gBAAgB,uBAAA,IAAI,2CAAa,EAAE,CAAC;QAEpC,MAAM,QAAQ,GAAG,MAAM,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAC/C,uBAAA,IAAI,qCAAO,MAAX,IAAI,EAAQ,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CACxC,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,kBAAkB,GAAG,uBAAA,IAAI,sFAAqB,MAAzB,IAAI,EAAsB,IAAI,CAAC,CAAC;QAE3D,OAAO;YACL,kBAAkB;YAClB,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;SAC3B,CAAC;IACJ,CAAC;CAgBF;AApMD,wDAoMC;gZALsB,YAA6B;IAChD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;QACvC,OAAO,EAAE,GAAG,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC7B,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC","sourcesContent":["import {\n createServicePolicy,\n DEFAULT_CIRCUIT_BREAK_DURATION,\n DEFAULT_MAX_CONSECUTIVE_FAILURES,\n DEFAULT_MAX_RETRIES,\n} from '@metamask/controller-utils';\nimport type { ServicePolicy } from '@metamask/controller-utils';\nimport type { IDisposable } from 'cockatiel';\n\nimport type { AbstractClientConfigApiService } from './abstract-client-config-api-service';\nimport { BASE_URL } from '../constants';\nimport type {\n FeatureFlags,\n ClientType,\n DistributionType,\n EnvironmentType,\n ServiceResponse,\n ApiDataResponse,\n} from '../remote-feature-flag-controller-types';\n\n/**\n * This service is responsible for fetching feature flags from the ClientConfig API.\n */\nexport class ClientConfigApiService implements AbstractClientConfigApiService {\n readonly #fetch: typeof fetch;\n\n readonly #policy: ServicePolicy;\n\n readonly #client: ClientType;\n\n readonly #distribution: DistributionType;\n\n readonly #environment: EnvironmentType;\n\n /**\n * Constructs a new ClientConfigApiService object.\n *\n * @param args - The arguments.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * If your JavaScript environment supports `fetch` natively, you'll probably\n * want to pass that; otherwise you can pass an equivalent (such as `fetch`\n * via `node-fetch`).\n * @param args.retries - Number of retry attempts for each fetch request.\n * @param args.maximumConsecutiveFailures - The maximum number of consecutive\n * failures allowed before breaking the circuit and pausing further fetch\n * attempts.\n * @param args.circuitBreakDuration - The amount of time to wait when the\n * circuit breaks from too many consecutive failures.\n * @param args.config - The configuration object, includes client,\n * distribution, and environment.\n * @param args.config.client - The client type (e.g., 'extension', 'mobile').\n * @param args.config.distribution - The distribution type (e.g., 'main',\n * 'flask').\n * @param args.config.environment - The environment type (e.g., 'prod', 'rc',\n * 'dev').\n */\n constructor(args: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n });\n\n /**\n * Constructs a new ClientConfigApiService object.\n *\n * @deprecated This signature is deprecated; please use the `onBreak` and\n * `onDegraded` methods instead.\n * @param args - The arguments.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * If your JavaScript environment supports `fetch` natively, you'll probably\n * want to pass that; otherwise you can pass an equivalent (such as `fetch`\n * via `node-fetch`).\n * @param args.retries - Number of retry attempts for each fetch request.\n * @param args.maximumConsecutiveFailures - The maximum number of consecutive\n * failures allowed before breaking the circuit and pausing further fetch\n * attempts.\n * @param args.circuitBreakDuration - The amount of time to wait when the\n * circuit breaks from too many consecutive failures.\n * @param args.onBreak - Callback for when the circuit breaks, useful\n * for capturing metrics about network failures.\n * @param args.onDegraded - Callback for when the API responds successfully\n * but takes too long to respond (5 seconds or more).\n * @param args.config - The configuration object, includes client,\n * distribution, and environment.\n * @param args.config.client - The client type (e.g., 'extension', 'mobile').\n * @param args.config.distribution - The distribution type (e.g., 'main',\n * 'flask').\n * @param args.config.environment - The environment type (e.g., 'prod', 'rc',\n * 'dev').\n */\n // eslint-disable-next-line @typescript-eslint/unified-signatures\n constructor(args: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n onBreak?: () => void;\n onDegraded?: () => void;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n });\n\n constructor({\n fetch: fetchFunction,\n retries = DEFAULT_MAX_RETRIES,\n maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES,\n circuitBreakDuration = DEFAULT_CIRCUIT_BREAK_DURATION,\n onBreak,\n onDegraded,\n config,\n }: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n onBreak?: () => void;\n onDegraded?: () => void;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n }) {\n this.#fetch = fetchFunction;\n this.#client = config.client;\n this.#distribution = config.distribution;\n this.#environment = config.environment;\n\n this.#policy = createServicePolicy({\n maxRetries: retries,\n maxConsecutiveFailures: maximumConsecutiveFailures,\n circuitBreakDuration,\n });\n if (onBreak) {\n this.#policy.onBreak(onBreak);\n }\n if (onDegraded) {\n this.#policy.onDegraded(onDegraded);\n }\n }\n\n /**\n * Listens for when the request to the API fails too many times in a row.\n *\n * @param args - The same arguments that {@link ServicePolicy.onBreak}\n * takes.\n * @returns What {@link ServicePolicy.onBreak} returns.\n */\n onBreak(...args: Parameters<ServicePolicy['onBreak']>): IDisposable {\n return this.#policy.onBreak(...args);\n }\n\n /**\n * Listens for when the API is degraded.\n *\n * @param args - The same arguments that {@link ServicePolicy.onDegraded}\n * takes.\n * @returns What {@link ServicePolicy.onDegraded} returns.\n */\n onDegraded(...args: Parameters<ServicePolicy['onDegraded']>): IDisposable {\n return this.#policy.onDegraded(...args);\n }\n\n /**\n * Fetches feature flags from the API with specific client, distribution, and environment parameters.\n * Provides structured error handling, including fallback to cached data if available.\n *\n * @returns An object of feature flags and their boolean values or a structured error object.\n */\n public async fetchRemoteFeatureFlags(): Promise<ServiceResponse> {\n const url = `${BASE_URL}/flags?client=${this.#client}&distribution=${\n this.#distribution\n }&environment=${this.#environment}`;\n\n const response = await this.#policy.execute(() =>\n this.#fetch(url, { cache: 'no-cache' }),\n );\n\n if (!response.ok) {\n throw new Error('Failed to fetch remote feature flags');\n }\n\n const data = await response.json();\n\n if (!Array.isArray(data)) {\n throw new Error('Feature flags api did not return an array');\n }\n\n const remoteFeatureFlags = this.#flattenFeatureFlags(data);\n\n return {\n remoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n }\n\n /**\n * Flattens an array of feature flag objects into a single feature flags object.\n *\n * @param responseData - Array of objects containing feature flag key-value pairs\n * @returns A single object containing all feature flags merged together\n * @example\n * // Input: [{ flag1: true }, { flag2: [] }]\n * // Output: { flag1: true, flag2: [] }\n */\n #flattenFeatureFlags(responseData: ApiDataResponse): FeatureFlags {\n return responseData.reduce((acc, curr) => {\n return { ...acc, ...curr };\n }, {});\n }\n}\n"]}
import type { ServicePolicy } from "@metamask/controller-utils";
import type { IDisposable } from "cockatiel";
import type { AbstractClientConfigApiService } from "./abstract-client-config-api-service.cjs";

@@ -90,3 +91,3 @@ import type { ClientType, DistributionType, EnvironmentType, ServiceResponse } from "../remote-feature-flag-controller-types.cjs";

*/
onBreak(...args: Parameters<ServicePolicy['onBreak']>): import("cockatiel").IDisposable;
onBreak(...args: Parameters<ServicePolicy['onBreak']>): IDisposable;
/**

@@ -99,3 +100,3 @@ * Listens for when the API is degraded.

*/
onDegraded(...args: Parameters<ServicePolicy['onDegraded']>): import("cockatiel").IDisposable;
onDegraded(...args: Parameters<ServicePolicy['onDegraded']>): IDisposable;
/**

@@ -108,13 +109,3 @@ * Fetches feature flags from the API with specific client, distribution, and environment parameters.

fetchRemoteFeatureFlags(): Promise<ServiceResponse>;
/**
* Flattens an array of feature flag objects into a single feature flags object.
*
* @param responseData - Array of objects containing feature flag key-value pairs
* @returns A single object containing all feature flags merged together
* @example
* // Input: [{ flag1: true }, { flag2: [] }]
* // Output: { flag1: true, flag2: [] }
*/
private flattenFeatureFlags;
}
//# sourceMappingURL=client-config-api-service.d.cts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"client-config-api-service.d.cts","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAEhE,OAAO,KAAK,EAAE,8BAA8B,EAAE,iDAA6C;AAE3F,OAAO,KAAK,EAEV,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EAEhB,oDAAgD;AAEjD;;GAEG;AACH,qBAAa,sBAAuB,YAAW,8BAA8B;;IAW3E;;;;;;;;;;;;;;;;;;;;;OAqBG;gBACS,IAAI,EAAE;QAChB,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,0BAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC;YACnB,YAAY,EAAE,gBAAgB,CAAC;YAC/B,WAAW,EAAE,eAAe,CAAC;SAC9B,CAAC;KACH;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;gBAES,IAAI,EAAE;QAChB,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,0BAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;QACxB,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC;YACnB,YAAY,EAAE,gBAAgB,CAAC;YAC/B,WAAW,EAAE,eAAe,CAAC;SAC9B,CAAC;KACH;IAyCD;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAIrD;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IAI3D;;;;;OAKG;IACU,uBAAuB,IAAI,OAAO,CAAC,eAAe,CAAC;IA2BhE;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;CAK5B"}
{"version":3,"file":"client-config-api-service.d.cts","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB;AAE7C,OAAO,KAAK,EAAE,8BAA8B,EAAE,iDAA6C;AAE3F,OAAO,KAAK,EAEV,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EAEhB,oDAAgD;AAEjD;;GAEG;AACH,qBAAa,sBAAuB,YAAW,8BAA8B;;IAW3E;;;;;;;;;;;;;;;;;;;;;OAqBG;gBACS,IAAI,EAAE;QAChB,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,0BAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC;YACnB,YAAY,EAAE,gBAAgB,CAAC;YAC/B,WAAW,EAAE,eAAe,CAAC;SAC9B,CAAC;KACH;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;gBAES,IAAI,EAAE;QAChB,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,0BAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;QACxB,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC;YACnB,YAAY,EAAE,gBAAgB,CAAC;YAC/B,WAAW,EAAE,eAAe,CAAC;SAC9B,CAAC;KACH;IAyCD;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,GAAG,WAAW;IAInE;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,GAAG,WAAW;IAIzE;;;;;OAKG;IACU,uBAAuB,IAAI,OAAO,CAAC,eAAe,CAAC;CAyCjE"}
import type { ServicePolicy } from "@metamask/controller-utils";
import type { IDisposable } from "cockatiel";
import type { AbstractClientConfigApiService } from "./abstract-client-config-api-service.mjs";

@@ -90,3 +91,3 @@ import type { ClientType, DistributionType, EnvironmentType, ServiceResponse } from "../remote-feature-flag-controller-types.mjs";

*/
onBreak(...args: Parameters<ServicePolicy['onBreak']>): import("cockatiel").IDisposable;
onBreak(...args: Parameters<ServicePolicy['onBreak']>): IDisposable;
/**

@@ -99,3 +100,3 @@ * Listens for when the API is degraded.

*/
onDegraded(...args: Parameters<ServicePolicy['onDegraded']>): import("cockatiel").IDisposable;
onDegraded(...args: Parameters<ServicePolicy['onDegraded']>): IDisposable;
/**

@@ -108,13 +109,3 @@ * Fetches feature flags from the API with specific client, distribution, and environment parameters.

fetchRemoteFeatureFlags(): Promise<ServiceResponse>;
/**
* Flattens an array of feature flag objects into a single feature flags object.
*
* @param responseData - Array of objects containing feature flag key-value pairs
* @returns A single object containing all feature flags merged together
* @example
* // Input: [{ flag1: true }, { flag2: [] }]
* // Output: { flag1: true, flag2: [] }
*/
private flattenFeatureFlags;
}
//# sourceMappingURL=client-config-api-service.d.mts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"client-config-api-service.d.mts","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAEhE,OAAO,KAAK,EAAE,8BAA8B,EAAE,iDAA6C;AAE3F,OAAO,KAAK,EAEV,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EAEhB,oDAAgD;AAEjD;;GAEG;AACH,qBAAa,sBAAuB,YAAW,8BAA8B;;IAW3E;;;;;;;;;;;;;;;;;;;;;OAqBG;gBACS,IAAI,EAAE;QAChB,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,0BAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC;YACnB,YAAY,EAAE,gBAAgB,CAAC;YAC/B,WAAW,EAAE,eAAe,CAAC;SAC9B,CAAC;KACH;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;gBAES,IAAI,EAAE;QAChB,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,0BAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;QACxB,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC;YACnB,YAAY,EAAE,gBAAgB,CAAC;YAC/B,WAAW,EAAE,eAAe,CAAC;SAC9B,CAAC;KACH;IAyCD;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAIrD;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IAI3D;;;;;OAKG;IACU,uBAAuB,IAAI,OAAO,CAAC,eAAe,CAAC;IA2BhE;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;CAK5B"}
{"version":3,"file":"client-config-api-service.d.mts","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB;AAE7C,OAAO,KAAK,EAAE,8BAA8B,EAAE,iDAA6C;AAE3F,OAAO,KAAK,EAEV,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EAEhB,oDAAgD;AAEjD;;GAEG;AACH,qBAAa,sBAAuB,YAAW,8BAA8B;;IAW3E;;;;;;;;;;;;;;;;;;;;;OAqBG;gBACS,IAAI,EAAE;QAChB,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,0BAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC;YACnB,YAAY,EAAE,gBAAgB,CAAC;YAC/B,WAAW,EAAE,eAAe,CAAC;SAC9B,CAAC;KACH;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;gBAES,IAAI,EAAE;QAChB,KAAK,EAAE,OAAO,KAAK,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,0BAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;QACxB,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC;YACnB,YAAY,EAAE,gBAAgB,CAAC;YAC/B,WAAW,EAAE,eAAe,CAAC;SAC9B,CAAC;KACH;IAyCD;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,GAAG,WAAW;IAInE;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,GAAG,WAAW;IAIzE;;;;;OAKG;IACU,uBAAuB,IAAI,OAAO,CAAC,eAAe,CAAC;CAyCjE"}

@@ -12,3 +12,3 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {

};
var _ClientConfigApiService_fetch, _ClientConfigApiService_policy, _ClientConfigApiService_client, _ClientConfigApiService_distribution, _ClientConfigApiService_environment;
var _ClientConfigApiService_instances, _ClientConfigApiService_fetch, _ClientConfigApiService_policy, _ClientConfigApiService_client, _ClientConfigApiService_distribution, _ClientConfigApiService_environment, _ClientConfigApiService_flattenFeatureFlags;
import { createServicePolicy, DEFAULT_CIRCUIT_BREAK_DURATION, DEFAULT_MAX_CONSECUTIVE_FAILURES, DEFAULT_MAX_RETRIES } from "@metamask/controller-utils";

@@ -21,2 +21,3 @@ import { BASE_URL } from "../constants.mjs";

constructor({ fetch: fetchFunction, retries = DEFAULT_MAX_RETRIES, maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES, circuitBreakDuration = DEFAULT_CIRCUIT_BREAK_DURATION, onBreak, onDegraded, config, }) {
_ClientConfigApiService_instances.add(this);
_ClientConfigApiService_fetch.set(this, void 0);

@@ -79,3 +80,3 @@ _ClientConfigApiService_policy.set(this, void 0);

}
const remoteFeatureFlags = this.flattenFeatureFlags(data);
const remoteFeatureFlags = __classPrivateFieldGet(this, _ClientConfigApiService_instances, "m", _ClientConfigApiService_flattenFeatureFlags).call(this, data);
return {

@@ -86,18 +87,8 @@ remoteFeatureFlags,

}
/**
* Flattens an array of feature flag objects into a single feature flags object.
*
* @param responseData - Array of objects containing feature flag key-value pairs
* @returns A single object containing all feature flags merged together
* @example
* // Input: [{ flag1: true }, { flag2: [] }]
* // Output: { flag1: true, flag2: [] }
*/
flattenFeatureFlags(responseData) {
return responseData.reduce((acc, curr) => {
return { ...acc, ...curr };
}, {});
}
}
_ClientConfigApiService_fetch = new WeakMap(), _ClientConfigApiService_policy = new WeakMap(), _ClientConfigApiService_client = new WeakMap(), _ClientConfigApiService_distribution = new WeakMap(), _ClientConfigApiService_environment = new WeakMap();
_ClientConfigApiService_fetch = new WeakMap(), _ClientConfigApiService_policy = new WeakMap(), _ClientConfigApiService_client = new WeakMap(), _ClientConfigApiService_distribution = new WeakMap(), _ClientConfigApiService_environment = new WeakMap(), _ClientConfigApiService_instances = new WeakSet(), _ClientConfigApiService_flattenFeatureFlags = function _ClientConfigApiService_flattenFeatureFlags(responseData) {
return responseData.reduce((acc, curr) => {
return { ...acc, ...curr };
}, {});
};
//# sourceMappingURL=client-config-api-service.mjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"client-config-api-service.mjs","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EACL,mBAAmB,EACnB,8BAA8B,EAC9B,gCAAgC,EAChC,mBAAmB,EACpB,mCAAmC;AAIpC,OAAO,EAAE,QAAQ,EAAE,yBAAqB;AAUxC;;GAEG;AACH,MAAM,OAAO,sBAAsB;IAwFjC,YAAY,EACV,KAAK,EAAE,aAAa,EACpB,OAAO,GAAG,mBAAmB,EAC7B,0BAA0B,GAAG,gCAAgC,EAC7D,oBAAoB,GAAG,8BAA8B,EACrD,OAAO,EACP,UAAU,EACV,MAAM,GAaP;QA3GQ,gDAAqB;QAErB,iDAAuB;QAEvB,iDAAoB;QAEpB,uDAAgC;QAEhC,sDAA8B;QAoGrC,uBAAA,IAAI,iCAAU,aAAa,MAAA,CAAC;QAC5B,uBAAA,IAAI,kCAAW,MAAM,CAAC,MAAM,MAAA,CAAC;QAC7B,uBAAA,IAAI,wCAAiB,MAAM,CAAC,YAAY,MAAA,CAAC;QACzC,uBAAA,IAAI,uCAAgB,MAAM,CAAC,WAAW,MAAA,CAAC;QAEvC,uBAAA,IAAI,kCAAW,mBAAmB,CAAC;YACjC,UAAU,EAAE,OAAO;YACnB,sBAAsB,EAAE,0BAA0B;YAClD,oBAAoB;SACrB,CAAC,MAAA,CAAC;QACH,IAAI,OAAO,EAAE,CAAC;YACZ,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,UAAU,EAAE,CAAC;YACf,uBAAA,IAAI,sCAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,IAA0C;QACnD,OAAO,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,IAA6C;QACzD,OAAO,uBAAA,IAAI,sCAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,uBAAuB;QAClC,MAAM,GAAG,GAAG,GAAG,QAAQ,iBAAiB,uBAAA,IAAI,sCAAQ,iBAClD,uBAAA,IAAI,4CACN,gBAAgB,uBAAA,IAAI,2CAAa,EAAE,CAAC;QAEpC,MAAM,QAAQ,GAAG,MAAM,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAC/C,uBAAA,IAAI,qCAAO,MAAX,IAAI,EAAQ,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CACxC,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,kBAAkB,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAE1D,OAAO;YACL,kBAAkB;YAClB,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;SAC3B,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACK,mBAAmB,CAAC,YAA6B;QACvD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;YACvC,OAAO,EAAE,GAAG,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;QAC7B,CAAC,EAAE,EAAE,CAAC,CAAC;IACT,CAAC;CACF","sourcesContent":["import {\n createServicePolicy,\n DEFAULT_CIRCUIT_BREAK_DURATION,\n DEFAULT_MAX_CONSECUTIVE_FAILURES,\n DEFAULT_MAX_RETRIES,\n} from '@metamask/controller-utils';\nimport type { ServicePolicy } from '@metamask/controller-utils';\n\nimport type { AbstractClientConfigApiService } from './abstract-client-config-api-service';\nimport { BASE_URL } from '../constants';\nimport type {\n FeatureFlags,\n ClientType,\n DistributionType,\n EnvironmentType,\n ServiceResponse,\n ApiDataResponse,\n} from '../remote-feature-flag-controller-types';\n\n/**\n * This service is responsible for fetching feature flags from the ClientConfig API.\n */\nexport class ClientConfigApiService implements AbstractClientConfigApiService {\n readonly #fetch: typeof fetch;\n\n readonly #policy: ServicePolicy;\n\n readonly #client: ClientType;\n\n readonly #distribution: DistributionType;\n\n readonly #environment: EnvironmentType;\n\n /**\n * Constructs a new ClientConfigApiService object.\n *\n * @param args - The arguments.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * If your JavaScript environment supports `fetch` natively, you'll probably\n * want to pass that; otherwise you can pass an equivalent (such as `fetch`\n * via `node-fetch`).\n * @param args.retries - Number of retry attempts for each fetch request.\n * @param args.maximumConsecutiveFailures - The maximum number of consecutive\n * failures allowed before breaking the circuit and pausing further fetch\n * attempts.\n * @param args.circuitBreakDuration - The amount of time to wait when the\n * circuit breaks from too many consecutive failures.\n * @param args.config - The configuration object, includes client,\n * distribution, and environment.\n * @param args.config.client - The client type (e.g., 'extension', 'mobile').\n * @param args.config.distribution - The distribution type (e.g., 'main',\n * 'flask').\n * @param args.config.environment - The environment type (e.g., 'prod', 'rc',\n * 'dev').\n */\n constructor(args: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n });\n\n /**\n * Constructs a new ClientConfigApiService object.\n *\n * @deprecated This signature is deprecated; please use the `onBreak` and\n * `onDegraded` methods instead.\n * @param args - The arguments.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * If your JavaScript environment supports `fetch` natively, you'll probably\n * want to pass that; otherwise you can pass an equivalent (such as `fetch`\n * via `node-fetch`).\n * @param args.retries - Number of retry attempts for each fetch request.\n * @param args.maximumConsecutiveFailures - The maximum number of consecutive\n * failures allowed before breaking the circuit and pausing further fetch\n * attempts.\n * @param args.circuitBreakDuration - The amount of time to wait when the\n * circuit breaks from too many consecutive failures.\n * @param args.onBreak - Callback for when the circuit breaks, useful\n * for capturing metrics about network failures.\n * @param args.onDegraded - Callback for when the API responds successfully\n * but takes too long to respond (5 seconds or more).\n * @param args.config - The configuration object, includes client,\n * distribution, and environment.\n * @param args.config.client - The client type (e.g., 'extension', 'mobile').\n * @param args.config.distribution - The distribution type (e.g., 'main',\n * 'flask').\n * @param args.config.environment - The environment type (e.g., 'prod', 'rc',\n * 'dev').\n */\n // eslint-disable-next-line @typescript-eslint/unified-signatures\n constructor(args: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n onBreak?: () => void;\n onDegraded?: () => void;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n });\n\n constructor({\n fetch: fetchFunction,\n retries = DEFAULT_MAX_RETRIES,\n maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES,\n circuitBreakDuration = DEFAULT_CIRCUIT_BREAK_DURATION,\n onBreak,\n onDegraded,\n config,\n }: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n onBreak?: () => void;\n onDegraded?: () => void;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n }) {\n this.#fetch = fetchFunction;\n this.#client = config.client;\n this.#distribution = config.distribution;\n this.#environment = config.environment;\n\n this.#policy = createServicePolicy({\n maxRetries: retries,\n maxConsecutiveFailures: maximumConsecutiveFailures,\n circuitBreakDuration,\n });\n if (onBreak) {\n this.#policy.onBreak(onBreak);\n }\n if (onDegraded) {\n this.#policy.onDegraded(onDegraded);\n }\n }\n\n /**\n * Listens for when the request to the API fails too many times in a row.\n *\n * @param args - The same arguments that {@link ServicePolicy.onBreak}\n * takes.\n * @returns What {@link ServicePolicy.onBreak} returns.\n */\n onBreak(...args: Parameters<ServicePolicy['onBreak']>) {\n return this.#policy.onBreak(...args);\n }\n\n /**\n * Listens for when the API is degraded.\n *\n * @param args - The same arguments that {@link ServicePolicy.onDegraded}\n * takes.\n * @returns What {@link ServicePolicy.onDegraded} returns.\n */\n onDegraded(...args: Parameters<ServicePolicy['onDegraded']>) {\n return this.#policy.onDegraded(...args);\n }\n\n /**\n * Fetches feature flags from the API with specific client, distribution, and environment parameters.\n * Provides structured error handling, including fallback to cached data if available.\n *\n * @returns An object of feature flags and their boolean values or a structured error object.\n */\n public async fetchRemoteFeatureFlags(): Promise<ServiceResponse> {\n const url = `${BASE_URL}/flags?client=${this.#client}&distribution=${\n this.#distribution\n }&environment=${this.#environment}`;\n\n const response = await this.#policy.execute(() =>\n this.#fetch(url, { cache: 'no-cache' }),\n );\n\n if (!response.ok) {\n throw new Error('Failed to fetch remote feature flags');\n }\n\n const data = await response.json();\n\n if (!Array.isArray(data)) {\n throw new Error('Feature flags api did not return an array');\n }\n\n const remoteFeatureFlags = this.flattenFeatureFlags(data);\n\n return {\n remoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n }\n\n /**\n * Flattens an array of feature flag objects into a single feature flags object.\n *\n * @param responseData - Array of objects containing feature flag key-value pairs\n * @returns A single object containing all feature flags merged together\n * @example\n * // Input: [{ flag1: true }, { flag2: [] }]\n * // Output: { flag1: true, flag2: [] }\n */\n private flattenFeatureFlags(responseData: ApiDataResponse): FeatureFlags {\n return responseData.reduce((acc, curr) => {\n return { ...acc, ...curr };\n }, {});\n }\n}\n"]}
{"version":3,"file":"client-config-api-service.mjs","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EACL,mBAAmB,EACnB,8BAA8B,EAC9B,gCAAgC,EAChC,mBAAmB,EACpB,mCAAmC;AAKpC,OAAO,EAAE,QAAQ,EAAE,yBAAqB;AAUxC;;GAEG;AACH,MAAM,OAAO,sBAAsB;IAwFjC,YAAY,EACV,KAAK,EAAE,aAAa,EACpB,OAAO,GAAG,mBAAmB,EAC7B,0BAA0B,GAAG,gCAAgC,EAC7D,oBAAoB,GAAG,8BAA8B,EACrD,OAAO,EACP,UAAU,EACV,MAAM,GAaP;;QA3GQ,gDAAqB;QAErB,iDAAuB;QAEvB,iDAAoB;QAEpB,uDAAgC;QAEhC,sDAA8B;QAoGrC,uBAAA,IAAI,iCAAU,aAAa,MAAA,CAAC;QAC5B,uBAAA,IAAI,kCAAW,MAAM,CAAC,MAAM,MAAA,CAAC;QAC7B,uBAAA,IAAI,wCAAiB,MAAM,CAAC,YAAY,MAAA,CAAC;QACzC,uBAAA,IAAI,uCAAgB,MAAM,CAAC,WAAW,MAAA,CAAC;QAEvC,uBAAA,IAAI,kCAAW,mBAAmB,CAAC;YACjC,UAAU,EAAE,OAAO;YACnB,sBAAsB,EAAE,0BAA0B;YAClD,oBAAoB;SACrB,CAAC,MAAA,CAAC;QACH,IAAI,OAAO,EAAE,CAAC;YACZ,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,UAAU,EAAE,CAAC;YACf,uBAAA,IAAI,sCAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,IAA0C;QACnD,OAAO,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,IAA6C;QACzD,OAAO,uBAAA,IAAI,sCAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,uBAAuB;QAClC,MAAM,GAAG,GAAG,GAAG,QAAQ,iBAAiB,uBAAA,IAAI,sCAAQ,iBAClD,uBAAA,IAAI,4CACN,gBAAgB,uBAAA,IAAI,2CAAa,EAAE,CAAC;QAEpC,MAAM,QAAQ,GAAG,MAAM,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAC/C,uBAAA,IAAI,qCAAO,MAAX,IAAI,EAAQ,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CACxC,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,kBAAkB,GAAG,uBAAA,IAAI,sFAAqB,MAAzB,IAAI,EAAsB,IAAI,CAAC,CAAC;QAE3D,OAAO;YACL,kBAAkB;YAClB,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;SAC3B,CAAC;IACJ,CAAC;CAgBF;gZALsB,YAA6B;IAChD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;QACvC,OAAO,EAAE,GAAG,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC7B,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC","sourcesContent":["import {\n createServicePolicy,\n DEFAULT_CIRCUIT_BREAK_DURATION,\n DEFAULT_MAX_CONSECUTIVE_FAILURES,\n DEFAULT_MAX_RETRIES,\n} from '@metamask/controller-utils';\nimport type { ServicePolicy } from '@metamask/controller-utils';\nimport type { IDisposable } from 'cockatiel';\n\nimport type { AbstractClientConfigApiService } from './abstract-client-config-api-service';\nimport { BASE_URL } from '../constants';\nimport type {\n FeatureFlags,\n ClientType,\n DistributionType,\n EnvironmentType,\n ServiceResponse,\n ApiDataResponse,\n} from '../remote-feature-flag-controller-types';\n\n/**\n * This service is responsible for fetching feature flags from the ClientConfig API.\n */\nexport class ClientConfigApiService implements AbstractClientConfigApiService {\n readonly #fetch: typeof fetch;\n\n readonly #policy: ServicePolicy;\n\n readonly #client: ClientType;\n\n readonly #distribution: DistributionType;\n\n readonly #environment: EnvironmentType;\n\n /**\n * Constructs a new ClientConfigApiService object.\n *\n * @param args - The arguments.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * If your JavaScript environment supports `fetch` natively, you'll probably\n * want to pass that; otherwise you can pass an equivalent (such as `fetch`\n * via `node-fetch`).\n * @param args.retries - Number of retry attempts for each fetch request.\n * @param args.maximumConsecutiveFailures - The maximum number of consecutive\n * failures allowed before breaking the circuit and pausing further fetch\n * attempts.\n * @param args.circuitBreakDuration - The amount of time to wait when the\n * circuit breaks from too many consecutive failures.\n * @param args.config - The configuration object, includes client,\n * distribution, and environment.\n * @param args.config.client - The client type (e.g., 'extension', 'mobile').\n * @param args.config.distribution - The distribution type (e.g., 'main',\n * 'flask').\n * @param args.config.environment - The environment type (e.g., 'prod', 'rc',\n * 'dev').\n */\n constructor(args: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n });\n\n /**\n * Constructs a new ClientConfigApiService object.\n *\n * @deprecated This signature is deprecated; please use the `onBreak` and\n * `onDegraded` methods instead.\n * @param args - The arguments.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * If your JavaScript environment supports `fetch` natively, you'll probably\n * want to pass that; otherwise you can pass an equivalent (such as `fetch`\n * via `node-fetch`).\n * @param args.retries - Number of retry attempts for each fetch request.\n * @param args.maximumConsecutiveFailures - The maximum number of consecutive\n * failures allowed before breaking the circuit and pausing further fetch\n * attempts.\n * @param args.circuitBreakDuration - The amount of time to wait when the\n * circuit breaks from too many consecutive failures.\n * @param args.onBreak - Callback for when the circuit breaks, useful\n * for capturing metrics about network failures.\n * @param args.onDegraded - Callback for when the API responds successfully\n * but takes too long to respond (5 seconds or more).\n * @param args.config - The configuration object, includes client,\n * distribution, and environment.\n * @param args.config.client - The client type (e.g., 'extension', 'mobile').\n * @param args.config.distribution - The distribution type (e.g., 'main',\n * 'flask').\n * @param args.config.environment - The environment type (e.g., 'prod', 'rc',\n * 'dev').\n */\n // eslint-disable-next-line @typescript-eslint/unified-signatures\n constructor(args: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n onBreak?: () => void;\n onDegraded?: () => void;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n });\n\n constructor({\n fetch: fetchFunction,\n retries = DEFAULT_MAX_RETRIES,\n maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES,\n circuitBreakDuration = DEFAULT_CIRCUIT_BREAK_DURATION,\n onBreak,\n onDegraded,\n config,\n }: {\n fetch: typeof fetch;\n retries?: number;\n maximumConsecutiveFailures?: number;\n circuitBreakDuration?: number;\n onBreak?: () => void;\n onDegraded?: () => void;\n config: {\n client: ClientType;\n distribution: DistributionType;\n environment: EnvironmentType;\n };\n }) {\n this.#fetch = fetchFunction;\n this.#client = config.client;\n this.#distribution = config.distribution;\n this.#environment = config.environment;\n\n this.#policy = createServicePolicy({\n maxRetries: retries,\n maxConsecutiveFailures: maximumConsecutiveFailures,\n circuitBreakDuration,\n });\n if (onBreak) {\n this.#policy.onBreak(onBreak);\n }\n if (onDegraded) {\n this.#policy.onDegraded(onDegraded);\n }\n }\n\n /**\n * Listens for when the request to the API fails too many times in a row.\n *\n * @param args - The same arguments that {@link ServicePolicy.onBreak}\n * takes.\n * @returns What {@link ServicePolicy.onBreak} returns.\n */\n onBreak(...args: Parameters<ServicePolicy['onBreak']>): IDisposable {\n return this.#policy.onBreak(...args);\n }\n\n /**\n * Listens for when the API is degraded.\n *\n * @param args - The same arguments that {@link ServicePolicy.onDegraded}\n * takes.\n * @returns What {@link ServicePolicy.onDegraded} returns.\n */\n onDegraded(...args: Parameters<ServicePolicy['onDegraded']>): IDisposable {\n return this.#policy.onDegraded(...args);\n }\n\n /**\n * Fetches feature flags from the API with specific client, distribution, and environment parameters.\n * Provides structured error handling, including fallback to cached data if available.\n *\n * @returns An object of feature flags and their boolean values or a structured error object.\n */\n public async fetchRemoteFeatureFlags(): Promise<ServiceResponse> {\n const url = `${BASE_URL}/flags?client=${this.#client}&distribution=${\n this.#distribution\n }&environment=${this.#environment}`;\n\n const response = await this.#policy.execute(() =>\n this.#fetch(url, { cache: 'no-cache' }),\n );\n\n if (!response.ok) {\n throw new Error('Failed to fetch remote feature flags');\n }\n\n const data = await response.json();\n\n if (!Array.isArray(data)) {\n throw new Error('Feature flags api did not return an array');\n }\n\n const remoteFeatureFlags = this.#flattenFeatureFlags(data);\n\n return {\n remoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n }\n\n /**\n * Flattens an array of feature flag objects into a single feature flags object.\n *\n * @param responseData - Array of objects containing feature flag key-value pairs\n * @returns A single object containing all feature flags merged together\n * @example\n * // Input: [{ flag1: true }, { flag2: [] }]\n * // Output: { flag1: true, flag2: [] }\n */\n #flattenFeatureFlags(responseData: ApiDataResponse): FeatureFlags {\n return responseData.reduce((acc, curr) => {\n return { ...acc, ...curr };\n }, {});\n }\n}\n"]}

@@ -1,1 +0,1 @@

{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uFAA+E;AAAtE,6IAAA,2BAA2B,OAAA;AAUpC,mGAIgD;AAH9C,kIAAA,UAAU,OAAA;AACV,wIAAA,gBAAgB,OAAA;AAChB,uIAAA,eAAe,OAAA;AAIjB,uGAA+F;AAAtF,mIAAA,sBAAsB,OAAA;AAC/B,+EAAoF;AAA3E,4IAAA,iCAAiC,OAAA","sourcesContent":["export { RemoteFeatureFlagController } from './remote-feature-flag-controller';\nexport type {\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger,\n RemoteFeatureFlagControllerActions,\n RemoteFeatureFlagControllerGetStateAction,\n RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction,\n RemoteFeatureFlagControllerEvents,\n RemoteFeatureFlagControllerStateChangeEvent,\n} from './remote-feature-flag-controller';\nexport {\n ClientType,\n DistributionType,\n EnvironmentType,\n} from './remote-feature-flag-controller-types';\n\nexport type { FeatureFlags } from './remote-feature-flag-controller-types';\nexport { ClientConfigApiService } from './client-config-api-service/client-config-api-service';\nexport { generateDeterministicRandomNumber } from './utils/user-segmentation-utils';\n"]}
{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uFAA+E;AAAtE,6IAAA,2BAA2B,OAAA;AAapC,mGAIgD;AAH9C,kIAAA,UAAU,OAAA;AACV,wIAAA,gBAAgB,OAAA;AAChB,uIAAA,eAAe,OAAA;AAIjB,uGAA+F;AAAtF,mIAAA,sBAAsB,OAAA;AAC/B,+EAAoF;AAA3E,4IAAA,iCAAiC,OAAA","sourcesContent":["export { RemoteFeatureFlagController } from './remote-feature-flag-controller';\nexport type {\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger,\n RemoteFeatureFlagControllerActions,\n RemoteFeatureFlagControllerGetStateAction,\n RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction,\n RemoteFeatureFlagControllerSetFlagOverrideAction,\n RemoteFeatureFlagControllerRemoveFlagOverrideAction,\n RemoteFeatureFlagControllerClearAllFlagOverridesAction,\n RemoteFeatureFlagControllerEvents,\n RemoteFeatureFlagControllerStateChangeEvent,\n} from './remote-feature-flag-controller';\nexport {\n ClientType,\n DistributionType,\n EnvironmentType,\n} from './remote-feature-flag-controller-types';\n\nexport type { FeatureFlags } from './remote-feature-flag-controller-types';\nexport { ClientConfigApiService } from './client-config-api-service/client-config-api-service';\nexport { generateDeterministicRandomNumber } from './utils/user-segmentation-utils';\n"]}
export { RemoteFeatureFlagController } from "./remote-feature-flag-controller.cjs";
export type { RemoteFeatureFlagControllerState, RemoteFeatureFlagControllerMessenger, RemoteFeatureFlagControllerActions, RemoteFeatureFlagControllerGetStateAction, RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction, RemoteFeatureFlagControllerEvents, RemoteFeatureFlagControllerStateChangeEvent, } from "./remote-feature-flag-controller.cjs";
export type { RemoteFeatureFlagControllerState, RemoteFeatureFlagControllerMessenger, RemoteFeatureFlagControllerActions, RemoteFeatureFlagControllerGetStateAction, RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction, RemoteFeatureFlagControllerSetFlagOverrideAction, RemoteFeatureFlagControllerRemoveFlagOverrideAction, RemoteFeatureFlagControllerClearAllFlagOverridesAction, RemoteFeatureFlagControllerEvents, RemoteFeatureFlagControllerStateChangeEvent, } from "./remote-feature-flag-controller.cjs";
export { ClientType, DistributionType, EnvironmentType, } from "./remote-feature-flag-controller-types.cjs";

@@ -4,0 +4,0 @@ export type { FeatureFlags } from "./remote-feature-flag-controller-types.cjs";

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,6CAAyC;AAC/E,YAAY,EACV,gCAAgC,EAChC,oCAAoC,EACpC,kCAAkC,EAClC,yCAAyC,EACzC,yDAAyD,EACzD,iCAAiC,EACjC,2CAA2C,GAC5C,6CAAyC;AAC1C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,eAAe,GAChB,mDAA+C;AAEhD,YAAY,EAAE,YAAY,EAAE,mDAA+C;AAC3E,OAAO,EAAE,sBAAsB,EAAE,kEAA8D;AAC/F,OAAO,EAAE,iCAAiC,EAAE,4CAAwC"}
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,6CAAyC;AAC/E,YAAY,EACV,gCAAgC,EAChC,oCAAoC,EACpC,kCAAkC,EAClC,yCAAyC,EACzC,yDAAyD,EACzD,gDAAgD,EAChD,mDAAmD,EACnD,sDAAsD,EACtD,iCAAiC,EACjC,2CAA2C,GAC5C,6CAAyC;AAC1C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,eAAe,GAChB,mDAA+C;AAEhD,YAAY,EAAE,YAAY,EAAE,mDAA+C;AAC3E,OAAO,EAAE,sBAAsB,EAAE,kEAA8D;AAC/F,OAAO,EAAE,iCAAiC,EAAE,4CAAwC"}
export { RemoteFeatureFlagController } from "./remote-feature-flag-controller.mjs";
export type { RemoteFeatureFlagControllerState, RemoteFeatureFlagControllerMessenger, RemoteFeatureFlagControllerActions, RemoteFeatureFlagControllerGetStateAction, RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction, RemoteFeatureFlagControllerEvents, RemoteFeatureFlagControllerStateChangeEvent, } from "./remote-feature-flag-controller.mjs";
export type { RemoteFeatureFlagControllerState, RemoteFeatureFlagControllerMessenger, RemoteFeatureFlagControllerActions, RemoteFeatureFlagControllerGetStateAction, RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction, RemoteFeatureFlagControllerSetFlagOverrideAction, RemoteFeatureFlagControllerRemoveFlagOverrideAction, RemoteFeatureFlagControllerClearAllFlagOverridesAction, RemoteFeatureFlagControllerEvents, RemoteFeatureFlagControllerStateChangeEvent, } from "./remote-feature-flag-controller.mjs";
export { ClientType, DistributionType, EnvironmentType, } from "./remote-feature-flag-controller-types.mjs";

@@ -4,0 +4,0 @@ export type { FeatureFlags } from "./remote-feature-flag-controller-types.mjs";

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,6CAAyC;AAC/E,YAAY,EACV,gCAAgC,EAChC,oCAAoC,EACpC,kCAAkC,EAClC,yCAAyC,EACzC,yDAAyD,EACzD,iCAAiC,EACjC,2CAA2C,GAC5C,6CAAyC;AAC1C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,eAAe,GAChB,mDAA+C;AAEhD,YAAY,EAAE,YAAY,EAAE,mDAA+C;AAC3E,OAAO,EAAE,sBAAsB,EAAE,kEAA8D;AAC/F,OAAO,EAAE,iCAAiC,EAAE,4CAAwC"}
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,6CAAyC;AAC/E,YAAY,EACV,gCAAgC,EAChC,oCAAoC,EACpC,kCAAkC,EAClC,yCAAyC,EACzC,yDAAyD,EACzD,gDAAgD,EAChD,mDAAmD,EACnD,sDAAsD,EACtD,iCAAiC,EACjC,2CAA2C,GAC5C,6CAAyC;AAC1C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,eAAe,GAChB,mDAA+C;AAEhD,YAAY,EAAE,YAAY,EAAE,mDAA+C;AAC3E,OAAO,EAAE,sBAAsB,EAAE,kEAA8D;AAC/F,OAAO,EAAE,iCAAiC,EAAE,4CAAwC"}

@@ -1,1 +0,1 @@

{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,6CAAyC;AAU/E,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,eAAe,EAChB,mDAA+C;AAGhD,OAAO,EAAE,sBAAsB,EAAE,kEAA8D;AAC/F,OAAO,EAAE,iCAAiC,EAAE,4CAAwC","sourcesContent":["export { RemoteFeatureFlagController } from './remote-feature-flag-controller';\nexport type {\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger,\n RemoteFeatureFlagControllerActions,\n RemoteFeatureFlagControllerGetStateAction,\n RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction,\n RemoteFeatureFlagControllerEvents,\n RemoteFeatureFlagControllerStateChangeEvent,\n} from './remote-feature-flag-controller';\nexport {\n ClientType,\n DistributionType,\n EnvironmentType,\n} from './remote-feature-flag-controller-types';\n\nexport type { FeatureFlags } from './remote-feature-flag-controller-types';\nexport { ClientConfigApiService } from './client-config-api-service/client-config-api-service';\nexport { generateDeterministicRandomNumber } from './utils/user-segmentation-utils';\n"]}
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,6CAAyC;AAa/E,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,eAAe,EAChB,mDAA+C;AAGhD,OAAO,EAAE,sBAAsB,EAAE,kEAA8D;AAC/F,OAAO,EAAE,iCAAiC,EAAE,4CAAwC","sourcesContent":["export { RemoteFeatureFlagController } from './remote-feature-flag-controller';\nexport type {\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger,\n RemoteFeatureFlagControllerActions,\n RemoteFeatureFlagControllerGetStateAction,\n RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction,\n RemoteFeatureFlagControllerSetFlagOverrideAction,\n RemoteFeatureFlagControllerRemoveFlagOverrideAction,\n RemoteFeatureFlagControllerClearAllFlagOverridesAction,\n RemoteFeatureFlagControllerEvents,\n RemoteFeatureFlagControllerStateChangeEvent,\n} from './remote-feature-flag-controller';\nexport {\n ClientType,\n DistributionType,\n EnvironmentType,\n} from './remote-feature-flag-controller-types';\n\nexport type { FeatureFlags } from './remote-feature-flag-controller-types';\nexport { ClientConfigApiService } from './client-config-api-service/client-config-api-service';\nexport { generateDeterministicRandomNumber } from './utils/user-segmentation-utils';\n"]}

@@ -15,3 +15,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.RemoteFeatureFlagController = exports.getDefaultRemoteFeatureFlagControllerState = exports.DEFAULT_CACHE_DURATION = void 0;
exports.RemoteFeatureFlagController = exports.getDefaultRemoteFeatureFlagControllerState = exports.DEFAULT_CACHE_DURATION = exports.controllerName = void 0;
const base_controller_1 = require("@metamask/base-controller");

@@ -22,3 +22,3 @@ const utils_1 = require("@metamask/utils");

// === GENERAL ===
const controllerName = 'RemoteFeatureFlagController';
exports.controllerName = 'RemoteFeatureFlagController';
exports.DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day

@@ -32,2 +32,14 @@ const remoteFeatureFlagControllerMetadata = {

},
localOverrides: {
includeInStateLogs: true,
persist: true,
includeInDebugSnapshot: true,
usedInUi: true,
},
rawRemoteFeatureFlags: {
includeInStateLogs: true,
persist: true,
includeInDebugSnapshot: true,
usedInUi: false,
},
cacheTimestamp: {

@@ -48,2 +60,4 @@ includeInStateLogs: true,

remoteFeatureFlags: {},
localOverrides: {},
rawRemoteFeatureFlags: {},
cacheTimestamp: 0,

@@ -77,3 +91,3 @@ };

super({
name: controllerName,
name: exports.controllerName,
metadata: remoteFeatureFlagControllerMetadata,

@@ -135,2 +149,45 @@ messenger,

}
/**
* Sets a local override for a specific feature flag.
*
* @param flagName - The name of the feature flag to override.
* @param value - The override value for the feature flag.
*/
setFlagOverride(flagName, value) {
this.update(() => {
return {
...this.state,
localOverrides: {
...this.state.localOverrides,
[flagName]: value,
},
};
});
}
/**
* Clears the local override for a specific feature flag.
*
* @param flagName - The name of the feature flag to clear.
*/
removeFlagOverride(flagName) {
const newLocalOverrides = { ...this.state.localOverrides };
delete newLocalOverrides[flagName];
this.update(() => {
return {
...this.state,
localOverrides: newLocalOverrides,
};
});
}
/**
* Clears all local feature flag overrides.
*/
clearAllFlagOverrides() {
this.update(() => {
return {
...this.state,
localOverrides: {},
};
});
}
}

@@ -150,3 +207,5 @@ exports.RemoteFeatureFlagController = RemoteFeatureFlagController;

return {
...this.state,
remoteFeatureFlags: processedRemoteFeatureFlags,
rawRemoteFeatureFlags: remoteFeatureFlags,
cacheTimestamp: Date.now(),

@@ -153,0 +212,0 @@ };

@@ -1,1 +0,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 { BaseController } from '@metamask/base-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport { isValidSemVerVersion } from '@metamask/utils';\nimport type { Json, SemVerVersion } from '@metamask/utils';\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} from './utils/user-segmentation-utils';\nimport { isVersionFeatureFlag, getVersionData } from './utils/version';\n\n// === GENERAL ===\n\nconst 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: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n cacheTimestamp: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n};\n\n// === MESSENGER ===\n\n/**\n * The action to retrieve the state of the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n | RemoteFeatureFlagControllerGetStateAction\n | RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type RemoteFeatureFlagControllerMessenger = Messenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions,\n RemoteFeatureFlagControllerEvents\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 readonly #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n readonly #getMetaMetricsId: () => string;\n\n readonly #clientVersion: SemVerVersion;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The 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 - Returns metaMetricsId.\n * @param options.clientVersion - The current client version for version-based feature flag filtering. Must be a valid 3-part SemVer version string.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n clientVersion,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId: () => string;\n fetchInterval?: number;\n disabled?: boolean;\n clientVersion: string;\n }) {\n if (!isValidSemVerVersion(clientVersion)) {\n throw new Error(\n `Invalid clientVersion: \"${clientVersion}\". Must be a valid 3-part SemVer version string`,\n );\n }\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 this.#clientVersion = clientVersion;\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 */\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 */\n async #updateCache(remoteFeatureFlags: FeatureFlags) {\n const processedRemoteFeatureFlags =\n await this.#processRemoteFeatureFlags(remoteFeatureFlags);\n this.update(() => {\n return {\n remoteFeatureFlags: processedRemoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n /**\n * Processes a version-based feature flag to get the appropriate value for the current client version.\n *\n * @param flagValue - The feature flag value to process\n * @returns The processed value, or null if no version qualifies (skip this flag)\n */\n #processVersionBasedFlag(flagValue: Json): Json | null {\n if (!isVersionFeatureFlag(flagValue)) {\n return flagValue;\n }\n\n return getVersionData(flagValue, this.#clientVersion);\n }\n\n async #processRemoteFeatureFlags(\n remoteFeatureFlags: FeatureFlags,\n ): Promise<FeatureFlags> {\n const processedRemoteFeatureFlags: FeatureFlags = {};\n const metaMetricsId = this.#getMetaMetricsId();\n const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = this.#processVersionBasedFlag(\n remoteFeatureFlagValue,\n );\n if (processedValue === null) {\n continue;\n }\n\n if (Array.isArray(processedValue) && thresholdValue) {\n const selectedGroup = processedValue.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"]}
{"version":3,"file":"remote-feature-flag-controller.cjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseController } from '@metamask/base-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport { isValidSemVerVersion } from '@metamask/utils';\nimport type { Json, SemVerVersion } from '@metamask/utils';\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} from './utils/user-segmentation-utils';\nimport { isVersionFeatureFlag, getVersionData } from './utils/version';\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 localOverrides?: FeatureFlags;\n rawRemoteFeatureFlags?: FeatureFlags;\n cacheTimestamp: number;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n localOverrides: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n rawRemoteFeatureFlags: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n cacheTimestamp: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n};\n\n// === MESSENGER ===\n\n/**\n * The action to retrieve the state of the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerSetFlagOverrideAction = {\n type: `${typeof controllerName}:setFlagOverride`;\n handler: RemoteFeatureFlagController['setFlagOverride'];\n};\n\nexport type RemoteFeatureFlagControllerRemoveFlagOverrideAction = {\n type: `${typeof controllerName}:removeFlagOverride`;\n handler: RemoteFeatureFlagController['removeFlagOverride'];\n};\n\nexport type RemoteFeatureFlagControllerClearAllFlagOverridesAction = {\n type: `${typeof controllerName}:clearAllFlagOverrides`;\n handler: RemoteFeatureFlagController['clearAllFlagOverrides'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n | RemoteFeatureFlagControllerGetStateAction\n | RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction\n | RemoteFeatureFlagControllerSetFlagOverrideAction\n | RemoteFeatureFlagControllerRemoveFlagOverrideAction\n | RemoteFeatureFlagControllerClearAllFlagOverridesAction;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type RemoteFeatureFlagControllerMessenger = Messenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions,\n RemoteFeatureFlagControllerEvents\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 localOverrides: {},\n rawRemoteFeatureFlags: {},\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 readonly #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n readonly #getMetaMetricsId: () => string;\n\n readonly #clientVersion: SemVerVersion;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The 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 - Returns metaMetricsId.\n * @param options.clientVersion - The current client version for version-based feature flag filtering. Must be a valid 3-part SemVer version string.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n clientVersion,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId: () => string;\n fetchInterval?: number;\n disabled?: boolean;\n clientVersion: string;\n }) {\n if (!isValidSemVerVersion(clientVersion)) {\n throw new Error(\n `Invalid clientVersion: \"${clientVersion}\". Must be a valid 3-part SemVer version string`,\n );\n }\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 this.#clientVersion = clientVersion;\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 */\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 */\n async #updateCache(remoteFeatureFlags: FeatureFlags): Promise<void> {\n const processedRemoteFeatureFlags =\n await this.#processRemoteFeatureFlags(remoteFeatureFlags);\n this.update(() => {\n return {\n ...this.state,\n remoteFeatureFlags: processedRemoteFeatureFlags,\n rawRemoteFeatureFlags: remoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n /**\n * Processes a version-based feature flag to get the appropriate value for the current client version.\n *\n * @param flagValue - The feature flag value to process\n * @returns The processed value, or null if no version qualifies (skip this flag)\n */\n #processVersionBasedFlag(flagValue: Json): Json | null {\n if (!isVersionFeatureFlag(flagValue)) {\n return flagValue;\n }\n\n return getVersionData(flagValue, this.#clientVersion);\n }\n\n async #processRemoteFeatureFlags(\n remoteFeatureFlags: FeatureFlags,\n ): Promise<FeatureFlags> {\n const processedRemoteFeatureFlags: FeatureFlags = {};\n const metaMetricsId = this.#getMetaMetricsId();\n const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = this.#processVersionBasedFlag(\n remoteFeatureFlagValue,\n );\n if (processedValue === null) {\n continue;\n }\n\n if (Array.isArray(processedValue) && thresholdValue) {\n const selectedGroup = processedValue.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 /**\n * Sets a local override for a specific feature flag.\n *\n * @param flagName - The name of the feature flag to override.\n * @param value - The override value for the feature flag.\n */\n setFlagOverride(flagName: string, value: Json): void {\n this.update(() => {\n return {\n ...this.state,\n localOverrides: {\n ...this.state.localOverrides,\n [flagName]: value,\n },\n };\n });\n }\n\n /**\n * Clears the local override for a specific feature flag.\n *\n * @param flagName - The name of the feature flag to clear.\n */\n removeFlagOverride(flagName: string): void {\n const newLocalOverrides = { ...this.state.localOverrides };\n delete newLocalOverrides[flagName];\n this.update(() => {\n return {\n ...this.state,\n localOverrides: newLocalOverrides,\n };\n });\n }\n\n /**\n * Clears all local feature flag overrides.\n */\n clearAllFlagOverrides(): void {\n this.update(() => {\n return {\n ...this.state,\n localOverrides: {},\n };\n });\n }\n}\n"]}
import { BaseController } from "@metamask/base-controller";
import type { ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
import type { Messenger } from "@metamask/messenger";
import type { Json } from "@metamask/utils";
import type { AbstractClientConfigApiService } from "./client-config-api-service/abstract-client-config-api-service.cjs";
import type { FeatureFlags } from "./remote-feature-flag-controller-types.cjs";
declare const controllerName = "RemoteFeatureFlagController";
export declare const controllerName = "RemoteFeatureFlagController";
export declare const DEFAULT_CACHE_DURATION: number;
export type RemoteFeatureFlagControllerState = {
remoteFeatureFlags: FeatureFlags;
localOverrides?: FeatureFlags;
rawRemoteFeatureFlags?: FeatureFlags;
cacheTimestamp: number;

@@ -20,3 +23,15 @@ };

};
export type RemoteFeatureFlagControllerActions = RemoteFeatureFlagControllerGetStateAction | RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction;
export type RemoteFeatureFlagControllerSetFlagOverrideAction = {
type: `${typeof controllerName}:setFlagOverride`;
handler: RemoteFeatureFlagController['setFlagOverride'];
};
export type RemoteFeatureFlagControllerRemoveFlagOverrideAction = {
type: `${typeof controllerName}:removeFlagOverride`;
handler: RemoteFeatureFlagController['removeFlagOverride'];
};
export type RemoteFeatureFlagControllerClearAllFlagOverridesAction = {
type: `${typeof controllerName}:clearAllFlagOverrides`;
handler: RemoteFeatureFlagController['clearAllFlagOverrides'];
};
export type RemoteFeatureFlagControllerActions = RemoteFeatureFlagControllerGetStateAction | RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction | RemoteFeatureFlagControllerSetFlagOverrideAction | RemoteFeatureFlagControllerRemoveFlagOverrideAction | RemoteFeatureFlagControllerClearAllFlagOverridesAction;
export type RemoteFeatureFlagControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, RemoteFeatureFlagControllerState>;

@@ -75,4 +90,20 @@ export type RemoteFeatureFlagControllerEvents = RemoteFeatureFlagControllerStateChangeEvent;

disable(): void;
/**
* Sets a local override for a specific feature flag.
*
* @param flagName - The name of the feature flag to override.
* @param value - The override value for the feature flag.
*/
setFlagOverride(flagName: string, value: Json): void;
/**
* Clears the local override for a specific feature flag.
*
* @param flagName - The name of the feature flag to clear.
*/
removeFlagOverride(flagName: string): void;
/**
* Clears all local feature flag overrides.
*/
clearAllFlagOverrides(): void;
}
export {};
//# sourceMappingURL=remote-feature-flag-controller.d.cts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"remote-feature-flag-controller.d.cts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC3B,kCAAkC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAIrD,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAAuE;AACrH,OAAO,KAAK,EACV,YAAY,EAGb,mDAA+C;AAShD,QAAA,MAAM,cAAc,gCAAgC,CAAC;AACrD,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAI1D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAmBF;;GAEG;AACH,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,yDAAyD,GAAG;IACtE,IAAI,EAAE,GAAG,OAAO,cAAc,2BAA2B,CAAC;IAC1D,OAAO,EAAE,2BAA2B,CAAC,0BAA0B,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAC1C,yCAAyC,GACzC,yDAAyD,CAAC;AAE9D,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,oCAAoC,GAAG,SAAS,CAC1D,OAAO,cAAc,EACrB,kCAAkC,EAClC,iCAAiC,CAClC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAK7F;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAaC;;;;;;;;;;;OAWG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,EAChB,aAAa,GACd,EAAE;QACD,SAAS,EAAE,oCAAoC,CAAC;QAChD,KAAK,CAAC,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAClD,sBAAsB,EAAE,8BAA8B,CAAC;QACvD,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;KACvB;IAiCD;;;;;OAKG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IA+F/C;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
{"version":3,"file":"remote-feature-flag-controller.d.cts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"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"}
import { BaseController } from "@metamask/base-controller";
import type { ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
import type { Messenger } from "@metamask/messenger";
import type { Json } from "@metamask/utils";
import type { AbstractClientConfigApiService } from "./client-config-api-service/abstract-client-config-api-service.mjs";
import type { FeatureFlags } from "./remote-feature-flag-controller-types.mjs";
declare const controllerName = "RemoteFeatureFlagController";
export declare const controllerName = "RemoteFeatureFlagController";
export declare const DEFAULT_CACHE_DURATION: number;
export type RemoteFeatureFlagControllerState = {
remoteFeatureFlags: FeatureFlags;
localOverrides?: FeatureFlags;
rawRemoteFeatureFlags?: FeatureFlags;
cacheTimestamp: number;

@@ -20,3 +23,15 @@ };

};
export type RemoteFeatureFlagControllerActions = RemoteFeatureFlagControllerGetStateAction | RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction;
export type RemoteFeatureFlagControllerSetFlagOverrideAction = {
type: `${typeof controllerName}:setFlagOverride`;
handler: RemoteFeatureFlagController['setFlagOverride'];
};
export type RemoteFeatureFlagControllerRemoveFlagOverrideAction = {
type: `${typeof controllerName}:removeFlagOverride`;
handler: RemoteFeatureFlagController['removeFlagOverride'];
};
export type RemoteFeatureFlagControllerClearAllFlagOverridesAction = {
type: `${typeof controllerName}:clearAllFlagOverrides`;
handler: RemoteFeatureFlagController['clearAllFlagOverrides'];
};
export type RemoteFeatureFlagControllerActions = RemoteFeatureFlagControllerGetStateAction | RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction | RemoteFeatureFlagControllerSetFlagOverrideAction | RemoteFeatureFlagControllerRemoveFlagOverrideAction | RemoteFeatureFlagControllerClearAllFlagOverridesAction;
export type RemoteFeatureFlagControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, RemoteFeatureFlagControllerState>;

@@ -75,4 +90,20 @@ export type RemoteFeatureFlagControllerEvents = RemoteFeatureFlagControllerStateChangeEvent;

disable(): void;
/**
* Sets a local override for a specific feature flag.
*
* @param flagName - The name of the feature flag to override.
* @param value - The override value for the feature flag.
*/
setFlagOverride(flagName: string, value: Json): void;
/**
* Clears the local override for a specific feature flag.
*
* @param flagName - The name of the feature flag to clear.
*/
removeFlagOverride(flagName: string): void;
/**
* Clears all local feature flag overrides.
*/
clearAllFlagOverrides(): void;
}
export {};
//# sourceMappingURL=remote-feature-flag-controller.d.mts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"remote-feature-flag-controller.d.mts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC3B,kCAAkC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAIrD,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAAuE;AACrH,OAAO,KAAK,EACV,YAAY,EAGb,mDAA+C;AAShD,QAAA,MAAM,cAAc,gCAAgC,CAAC;AACrD,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAI1D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAmBF;;GAEG;AACH,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,yDAAyD,GAAG;IACtE,IAAI,EAAE,GAAG,OAAO,cAAc,2BAA2B,CAAC;IAC1D,OAAO,EAAE,2BAA2B,CAAC,0BAA0B,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAC1C,yCAAyC,GACzC,yDAAyD,CAAC;AAE9D,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,oCAAoC,GAAG,SAAS,CAC1D,OAAO,cAAc,EACrB,kCAAkC,EAClC,iCAAiC,CAClC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAK7F;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAaC;;;;;;;;;;;OAWG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,EAChB,aAAa,GACd,EAAE;QACD,SAAS,EAAE,oCAAoC,CAAC;QAChD,KAAK,CAAC,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAClD,sBAAsB,EAAE,8BAA8B,CAAC;QACvD,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;KACvB;IAiCD;;;;;OAKG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IA+F/C;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
{"version":3,"file":"remote-feature-flag-controller.d.mts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC3B,kCAAkC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAErD,OAAO,KAAK,EAAE,IAAI,EAAiB,wBAAwB;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,CAAC,EAAE,YAAY,CAAC;IAC9B,qBAAqB,CAAC,EAAE,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AA+BF;;GAEG;AACH,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,yDAAyD,GAAG;IACtE,IAAI,EAAE,GAAG,OAAO,cAAc,2BAA2B,CAAC;IAC1D,OAAO,EAAE,2BAA2B,CAAC,0BAA0B,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,gDAAgD,GAAG;IAC7D,IAAI,EAAE,GAAG,OAAO,cAAc,kBAAkB,CAAC;IACjD,OAAO,EAAE,2BAA2B,CAAC,iBAAiB,CAAC,CAAC;CACzD,CAAC;AAEF,MAAM,MAAM,mDAAmD,GAAG;IAChE,IAAI,EAAE,GAAG,OAAO,cAAc,qBAAqB,CAAC;IACpD,OAAO,EAAE,2BAA2B,CAAC,oBAAoB,CAAC,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,sDAAsD,GAAG;IACnE,IAAI,EAAE,GAAG,OAAO,cAAc,wBAAwB,CAAC;IACvD,OAAO,EAAE,2BAA2B,CAAC,uBAAuB,CAAC,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAC1C,yCAAyC,GACzC,yDAAyD,GACzD,gDAAgD,GAChD,mDAAmD,GACnD,sDAAsD,CAAC;AAE3D,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,oCAAoC,GAAG,SAAS,CAC1D,OAAO,cAAc,EACrB,kCAAkC,EAClC,iCAAiC,CAClC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAO7F;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAaC;;;;;;;;;;;OAWG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,EAChB,aAAa,GACd,EAAE;QACD,SAAS,EAAE,oCAAoC,CAAC;QAChD,KAAK,CAAC,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAClD,sBAAsB,EAAE,8BAA8B,CAAC;QACvD,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;KACvB;IAiCD;;;;;OAKG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IAiG/C;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;IAIf;;;;;OAKG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI;IAYpD;;;;OAIG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAW1C;;OAEG;IACH,qBAAqB,IAAI,IAAI;CAQ9B"}

@@ -18,3 +18,3 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {

// === GENERAL ===
const controllerName = 'RemoteFeatureFlagController';
export const controllerName = 'RemoteFeatureFlagController';
export const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day

@@ -28,2 +28,14 @@ const remoteFeatureFlagControllerMetadata = {

},
localOverrides: {
includeInStateLogs: true,
persist: true,
includeInDebugSnapshot: true,
usedInUi: true,
},
rawRemoteFeatureFlags: {
includeInStateLogs: true,
persist: true,
includeInDebugSnapshot: true,
usedInUi: false,
},
cacheTimestamp: {

@@ -44,2 +56,4 @@ includeInStateLogs: true,

remoteFeatureFlags: {},
localOverrides: {},
rawRemoteFeatureFlags: {},
cacheTimestamp: 0,

@@ -129,2 +143,45 @@ };

}
/**
* Sets a local override for a specific feature flag.
*
* @param flagName - The name of the feature flag to override.
* @param value - The override value for the feature flag.
*/
setFlagOverride(flagName, value) {
this.update(() => {
return {
...this.state,
localOverrides: {
...this.state.localOverrides,
[flagName]: value,
},
};
});
}
/**
* Clears the local override for a specific feature flag.
*
* @param flagName - The name of the feature flag to clear.
*/
removeFlagOverride(flagName) {
const newLocalOverrides = { ...this.state.localOverrides };
delete newLocalOverrides[flagName];
this.update(() => {
return {
...this.state,
localOverrides: newLocalOverrides,
};
});
}
/**
* Clears all local feature flag overrides.
*/
clearAllFlagOverrides() {
this.update(() => {
return {
...this.state,
localOverrides: {},
};
});
}
}

@@ -143,3 +200,5 @@ _RemoteFeatureFlagController_fetchInterval = new WeakMap(), _RemoteFeatureFlagController_disabled = new WeakMap(), _RemoteFeatureFlagController_clientConfigApiService = new WeakMap(), _RemoteFeatureFlagController_inProgressFlagUpdate = new WeakMap(), _RemoteFeatureFlagController_getMetaMetricsId = new WeakMap(), _RemoteFeatureFlagController_clientVersion = new WeakMap(), _RemoteFeatureFlagController_instances = new WeakSet(), _RemoteFeatureFlagController_isCacheExpired = function _RemoteFeatureFlagController_isCacheExpired() {

return {
...this.state,
remoteFeatureFlags: processedRemoteFeatureFlags,
rawRemoteFeatureFlags: remoteFeatureFlags,
cacheTimestamp: Date.now(),

@@ -146,0 +205,0 @@ };

@@ -1,1 +0,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 { BaseController } from '@metamask/base-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport { isValidSemVerVersion } from '@metamask/utils';\nimport type { Json, SemVerVersion } from '@metamask/utils';\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} from './utils/user-segmentation-utils';\nimport { isVersionFeatureFlag, getVersionData } from './utils/version';\n\n// === GENERAL ===\n\nconst 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: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n cacheTimestamp: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n};\n\n// === MESSENGER ===\n\n/**\n * The action to retrieve the state of the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n | RemoteFeatureFlagControllerGetStateAction\n | RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type RemoteFeatureFlagControllerMessenger = Messenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions,\n RemoteFeatureFlagControllerEvents\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 readonly #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n readonly #getMetaMetricsId: () => string;\n\n readonly #clientVersion: SemVerVersion;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The 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 - Returns metaMetricsId.\n * @param options.clientVersion - The current client version for version-based feature flag filtering. Must be a valid 3-part SemVer version string.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n clientVersion,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId: () => string;\n fetchInterval?: number;\n disabled?: boolean;\n clientVersion: string;\n }) {\n if (!isValidSemVerVersion(clientVersion)) {\n throw new Error(\n `Invalid clientVersion: \"${clientVersion}\". Must be a valid 3-part SemVer version string`,\n );\n }\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 this.#clientVersion = clientVersion;\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 */\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 */\n async #updateCache(remoteFeatureFlags: FeatureFlags) {\n const processedRemoteFeatureFlags =\n await this.#processRemoteFeatureFlags(remoteFeatureFlags);\n this.update(() => {\n return {\n remoteFeatureFlags: processedRemoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n /**\n * Processes a version-based feature flag to get the appropriate value for the current client version.\n *\n * @param flagValue - The feature flag value to process\n * @returns The processed value, or null if no version qualifies (skip this flag)\n */\n #processVersionBasedFlag(flagValue: Json): Json | null {\n if (!isVersionFeatureFlag(flagValue)) {\n return flagValue;\n }\n\n return getVersionData(flagValue, this.#clientVersion);\n }\n\n async #processRemoteFeatureFlags(\n remoteFeatureFlags: FeatureFlags,\n ): Promise<FeatureFlags> {\n const processedRemoteFeatureFlags: FeatureFlags = {};\n const metaMetricsId = this.#getMetaMetricsId();\n const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = this.#processVersionBasedFlag(\n remoteFeatureFlagValue,\n );\n if (processedValue === null) {\n continue;\n }\n\n if (Array.isArray(processedValue) && thresholdValue) {\n const selectedGroup = processedValue.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"]}
{"version":3,"file":"remote-feature-flag-controller.mjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAM3D,OAAO,EAAE,oBAAoB,EAAE,wBAAwB;AASvD,OAAO,EACL,iCAAiC,EACjC,2BAA2B,EAC5B,4CAAwC;AACzC,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,4BAAwB;AAEvE,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;AAWnE,MAAM,mCAAmC,GAAG;IAC1C,kBAAkB,EAAE;QAClB,kBAAkB,EAAE,IAAI;QACxB,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,QAAQ,EAAE,IAAI;KACf;IACD,cAAc,EAAE;QACd,kBAAkB,EAAE,IAAI;QACxB,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,QAAQ,EAAE,IAAI;KACf;IACD,qBAAqB,EAAE;QACrB,kBAAkB,EAAE,IAAI;QACxB,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,QAAQ,EAAE,KAAK;KAChB;IACD,cAAc,EAAE;QACd,kBAAkB,EAAE,IAAI;QACxB,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,QAAQ,EAAE,KAAK;KAChB;CACF,CAAC;AAuDF;;;;GAIG;AACH,MAAM,UAAU,0CAA0C;IACxD,OAAO;QACL,kBAAkB,EAAE,EAAE;QACtB,cAAc,EAAE,EAAE;QAClB,qBAAqB,EAAE,EAAE;QACzB,cAAc,EAAE,CAAC;KAClB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,2BAA4B,SAAQ,cAIhD;IAaC;;;;;;;;;;;OAWG;IACH,YAAY,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAa,GAAG,sBAAsB,EACtC,QAAQ,GAAG,KAAK,EAChB,gBAAgB,EAChB,aAAa,GASd;QACC,IAAI,CAAC,oBAAoB,CAAC,aAAa,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CACb,2BAA2B,aAAa,iDAAiD,CAC1F,CAAC;QACJ,CAAC;QAED,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;;QAvDI,6DAAuB;QAEhC,wDAAmB;QAEV,sEAAwD;QAEjE,oEAAiD;QAExC,gEAAgC;QAEhC,6DAA8B;QA+CrC,uBAAA,IAAI,8CAAkB,aAAa,MAAA,CAAC;QACpC,uBAAA,IAAI,yCAAa,QAAQ,MAAA,CAAC;QAC1B,uBAAA,IAAI,uDAA2B,sBAAsB,MAAA,CAAC;QACtD,uBAAA,IAAI,iDAAqB,gBAAgB,MAAA,CAAC;QAC1C,uBAAA,IAAI,8CAAkB,aAAa,MAAA,CAAC;IACtC,CAAC;IAWD;;;;;OAKG;IACH,KAAK,CAAC,wBAAwB;QAC5B,IAAI,uBAAA,IAAI,6CAAU,IAAI,CAAC,uBAAA,IAAI,2FAAgB,MAApB,IAAI,CAAkB,EAAE,CAAC;YAC9C,OAAO;QACT,CAAC;QAED,IAAI,UAAU,CAAC;QAEf,IAAI,uBAAA,IAAI,yDAAsB,EAAE,CAAC;YAC/B,MAAM,uBAAA,IAAI,yDAAsB,CAAC;YACjC,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,uBAAA,IAAI,qDACF,uBAAA,IAAI,2DAAwB,CAAC,uBAAuB,EAAE,MAAA,CAAC;YAEzD,UAAU,GAAG,MAAM,uBAAA,IAAI,yDAAsB,CAAC;QAChD,CAAC;gBAAS,CAAC;YACT,uBAAA,IAAI,qDAAyB,SAAS,MAAA,CAAC;QACzC,CAAC;QAED,MAAM,uBAAA,IAAI,wFAAa,MAAjB,IAAI,EAAc,UAAU,CAAC,kBAAkB,CAAC,CAAC;IACzD,CAAC;IA2ED;;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;IAED;;;;;OAKG;IACH,eAAe,CAAC,QAAgB,EAAE,KAAW;QAC3C,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;YACf,OAAO;gBACL,GAAG,IAAI,CAAC,KAAK;gBACb,cAAc,EAAE;oBACd,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc;oBAC5B,CAAC,QAAQ,CAAC,EAAE,KAAK;iBAClB;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,kBAAkB,CAAC,QAAgB;QACjC,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;QAC3D,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;YACf,OAAO;gBACL,GAAG,IAAI,CAAC,KAAK;gBACb,cAAc,EAAE,iBAAiB;aAClC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,qBAAqB;QACnB,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;YACf,OAAO;gBACL,GAAG,IAAI,CAAC,KAAK;gBACb,cAAc,EAAE,EAAE;aACnB,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF;;IArKG,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,uBAAA,IAAI,kDAAe,CAAC;AACtE,CAAC;AAgCD;;;;GAIG;AACH,KAAK,mDAAc,kBAAgC;IACjD,MAAM,2BAA2B,GAC/B,MAAM,uBAAA,IAAI,sGAA2B,MAA/B,IAAI,EAA4B,kBAAkB,CAAC,CAAC;IAC5D,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;QACf,OAAO;YACL,GAAG,IAAI,CAAC,KAAK;YACb,kBAAkB,EAAE,2BAA2B;YAC/C,qBAAqB,EAAE,kBAAkB;YACzC,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;SAC3B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,uHAQwB,SAAe;IACtC,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,cAAc,CAAC,SAAS,EAAE,uBAAA,IAAI,kDAAe,CAAC,CAAC;AACxD,CAAC,2DAED,KAAK,iEACH,kBAAgC;IAEhC,MAAM,2BAA2B,GAAiB,EAAE,CAAC;IACrD,MAAM,aAAa,GAAG,uBAAA,IAAI,qDAAkB,MAAtB,IAAI,CAAoB,CAAC;IAC/C,MAAM,cAAc,GAAG,iCAAiC,CAAC,aAAa,CAAC,CAAC;IAExE,KAAK,MAAM,CACT,qBAAqB,EACrB,sBAAsB,EACvB,IAAI,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACxC,IAAI,cAAc,GAAG,uBAAA,IAAI,oGAAyB,MAA7B,IAAI,EACvB,sBAAsB,CACvB,CAAC;QACF,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;YAC5B,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,cAAc,EAAE,CAAC;YACpD,MAAM,aAAa,GAAG,cAAc,CAAC,IAAI,CACvC,CAAC,WAAW,EAAwC,EAAE;gBACpD,IAAI,CAAC,2BAA2B,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC9C,OAAO,KAAK,CAAC;gBACf,CAAC;gBAED,OAAO,cAAc,IAAI,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC;YACnD,CAAC,CACF,CAAC;YACF,IAAI,aAAa,EAAE,CAAC;gBAClB,cAAc,GAAG;oBACf,IAAI,EAAE,aAAa,CAAC,IAAI;oBACxB,KAAK,EAAE,aAAa,CAAC,KAAK;iBAC3B,CAAC;YACJ,CAAC;QACH,CAAC;QAED,2BAA2B,CAAC,qBAAqB,CAAC,GAAG,cAAc,CAAC;IACtE,CAAC;IACD,OAAO,2BAA2B,CAAC;AACrC,CAAC","sourcesContent":["import { BaseController } from '@metamask/base-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport { isValidSemVerVersion } from '@metamask/utils';\nimport type { Json, SemVerVersion } from '@metamask/utils';\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} from './utils/user-segmentation-utils';\nimport { isVersionFeatureFlag, getVersionData } from './utils/version';\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 localOverrides?: FeatureFlags;\n rawRemoteFeatureFlags?: FeatureFlags;\n cacheTimestamp: number;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n localOverrides: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n rawRemoteFeatureFlags: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n cacheTimestamp: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n};\n\n// === MESSENGER ===\n\n/**\n * The action to retrieve the state of the {@link RemoteFeatureFlagController}.\n */\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerSetFlagOverrideAction = {\n type: `${typeof controllerName}:setFlagOverride`;\n handler: RemoteFeatureFlagController['setFlagOverride'];\n};\n\nexport type RemoteFeatureFlagControllerRemoveFlagOverrideAction = {\n type: `${typeof controllerName}:removeFlagOverride`;\n handler: RemoteFeatureFlagController['removeFlagOverride'];\n};\n\nexport type RemoteFeatureFlagControllerClearAllFlagOverridesAction = {\n type: `${typeof controllerName}:clearAllFlagOverrides`;\n handler: RemoteFeatureFlagController['clearAllFlagOverrides'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n | RemoteFeatureFlagControllerGetStateAction\n | RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction\n | RemoteFeatureFlagControllerSetFlagOverrideAction\n | RemoteFeatureFlagControllerRemoveFlagOverrideAction\n | RemoteFeatureFlagControllerClearAllFlagOverridesAction;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type RemoteFeatureFlagControllerMessenger = Messenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions,\n RemoteFeatureFlagControllerEvents\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 localOverrides: {},\n rawRemoteFeatureFlags: {},\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 readonly #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n readonly #getMetaMetricsId: () => string;\n\n readonly #clientVersion: SemVerVersion;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The 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 - Returns metaMetricsId.\n * @param options.clientVersion - The current client version for version-based feature flag filtering. Must be a valid 3-part SemVer version string.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n clientVersion,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId: () => string;\n fetchInterval?: number;\n disabled?: boolean;\n clientVersion: string;\n }) {\n if (!isValidSemVerVersion(clientVersion)) {\n throw new Error(\n `Invalid clientVersion: \"${clientVersion}\". Must be a valid 3-part SemVer version string`,\n );\n }\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 this.#clientVersion = clientVersion;\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 */\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 */\n async #updateCache(remoteFeatureFlags: FeatureFlags): Promise<void> {\n const processedRemoteFeatureFlags =\n await this.#processRemoteFeatureFlags(remoteFeatureFlags);\n this.update(() => {\n return {\n ...this.state,\n remoteFeatureFlags: processedRemoteFeatureFlags,\n rawRemoteFeatureFlags: remoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n /**\n * Processes a version-based feature flag to get the appropriate value for the current client version.\n *\n * @param flagValue - The feature flag value to process\n * @returns The processed value, or null if no version qualifies (skip this flag)\n */\n #processVersionBasedFlag(flagValue: Json): Json | null {\n if (!isVersionFeatureFlag(flagValue)) {\n return flagValue;\n }\n\n return getVersionData(flagValue, this.#clientVersion);\n }\n\n async #processRemoteFeatureFlags(\n remoteFeatureFlags: FeatureFlags,\n ): Promise<FeatureFlags> {\n const processedRemoteFeatureFlags: FeatureFlags = {};\n const metaMetricsId = this.#getMetaMetricsId();\n const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = this.#processVersionBasedFlag(\n remoteFeatureFlagValue,\n );\n if (processedValue === null) {\n continue;\n }\n\n if (Array.isArray(processedValue) && thresholdValue) {\n const selectedGroup = processedValue.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 /**\n * Sets a local override for a specific feature flag.\n *\n * @param flagName - The name of the feature flag to override.\n * @param value - The override value for the feature flag.\n */\n setFlagOverride(flagName: string, value: Json): void {\n this.update(() => {\n return {\n ...this.state,\n localOverrides: {\n ...this.state.localOverrides,\n [flagName]: value,\n },\n };\n });\n }\n\n /**\n * Clears the local override for a specific feature flag.\n *\n * @param flagName - The name of the feature flag to clear.\n */\n removeFlagOverride(flagName: string): void {\n const newLocalOverrides = { ...this.state.localOverrides };\n delete newLocalOverrides[flagName];\n this.update(() => {\n return {\n ...this.state,\n localOverrides: newLocalOverrides,\n };\n });\n }\n\n /**\n * Clears all local feature flag overrides.\n */\n clearAllFlagOverrides(): void {\n this.update(() => {\n return {\n ...this.state,\n localOverrides: {},\n };\n });\n }\n}\n"]}
{
"name": "@metamask/remote-feature-flag-controller",
"version": "3.0.0",
"version": "3.1.0",
"description": "The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags",

@@ -5,0 +5,0 @@ "keywords": [