@metamask-previews/remote-feature-flag-controller 1.3.0-preview-59b4e66e → 1.3.0-preview-4c4eb52a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/CHANGELOG.md +0 -11
  2. package/dist/client-config-api-service/abstract-client-config-api-service.cjs.map +1 -1
  3. package/dist/client-config-api-service/abstract-client-config-api-service.d.cts +3 -13
  4. package/dist/client-config-api-service/abstract-client-config-api-service.d.cts.map +1 -1
  5. package/dist/client-config-api-service/abstract-client-config-api-service.d.mts +3 -13
  6. package/dist/client-config-api-service/abstract-client-config-api-service.d.mts.map +1 -1
  7. package/dist/client-config-api-service/abstract-client-config-api-service.mjs.map +1 -1
  8. package/dist/client-config-api-service/client-config-api-service.cjs +46 -30
  9. package/dist/client-config-api-service/client-config-api-service.cjs.map +1 -1
  10. package/dist/client-config-api-service/client-config-api-service.d.cts +12 -73
  11. package/dist/client-config-api-service/client-config-api-service.d.cts.map +1 -1
  12. package/dist/client-config-api-service/client-config-api-service.d.mts +12 -73
  13. package/dist/client-config-api-service/client-config-api-service.d.mts.map +1 -1
  14. package/dist/client-config-api-service/client-config-api-service.mjs +45 -29
  15. package/dist/client-config-api-service/client-config-api-service.mjs.map +1 -1
  16. package/dist/remote-feature-flag-controller.cjs +1 -1
  17. package/dist/remote-feature-flag-controller.cjs.map +1 -1
  18. package/dist/remote-feature-flag-controller.d.cts +3 -3
  19. package/dist/remote-feature-flag-controller.d.cts.map +1 -1
  20. package/dist/remote-feature-flag-controller.d.mts +3 -3
  21. package/dist/remote-feature-flag-controller.d.mts.map +1 -1
  22. package/dist/remote-feature-flag-controller.mjs +1 -1
  23. package/dist/remote-feature-flag-controller.mjs.map +1 -1
  24. package/package.json +4 -3
package/CHANGELOG.md CHANGED
@@ -7,17 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ### Added
11
-
12
- - Add `onBreak` and `onDegraded` methods to `ClientConfigApiService` ([#5109](https://github.com/MetaMask/core/pull/5109))
13
- - These serve the same purpose as the `onBreak` and `onDegraded` constructor options, but align more closely with the Cockatiel policy API.
14
-
15
- ### Changed
16
-
17
- - Deprecate `ClientConfigApiService` constructor options `onBreak` and `onDegraded` in favor of methods ([#5109](https://github.com/MetaMask/core/pull/5109))
18
- - Add `@metamask/controller-utils@^11.4.5` as a dependency ([#5109](https://github.com/MetaMask/core/pull/5109))
19
- - `cockatiel` should still be in the dependency tree because it's now a dependency of `@metamask/controller-utils`
20
-
21
10
  ## [1.3.0]
22
11
 
23
12
  ### Changed
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-client-config-api-service.cjs","sourceRoot":"","sources":["../../src/client-config-api-service/abstract-client-config-api-service.ts"],"names":[],"mappings":"","sourcesContent":["import type { ServicePolicy } from '@metamask/controller-utils';\n\nimport type { ServiceResponse } from '../remote-feature-flag-controller-types';\n\n/**\n * A service object responsible for fetching feature flags.\n */\nexport type AbstractClientConfigApiService = Partial<\n Pick<ServicePolicy, 'onBreak' | 'onDegraded'>\n> & {\n /**\n * Fetches feature flags from the API with specific client, distribution, and\n * environment parameters. Provides structured error handling, including\n * fallback to cached data if available.\n *\n * @returns An object of feature flags and their boolean values or a\n * structured error object.\n */\n fetchRemoteFeatureFlags(): Promise<ServiceResponse>;\n};\n"]}
1
+ {"version":3,"file":"abstract-client-config-api-service.cjs","sourceRoot":"","sources":["../../src/client-config-api-service/abstract-client-config-api-service.ts"],"names":[],"mappings":"","sourcesContent":["import type { PublicInterface } from '@metamask/utils';\n\nimport type { ClientConfigApiService } from './client-config-api-service';\n\n/**\n * A service object responsible for fetching feature flags.\n */\nexport type AbstractClientConfigApiService =\n PublicInterface<ClientConfigApiService>;\n"]}
@@ -1,17 +1,7 @@
1
- import type { ServicePolicy } from "@metamask/controller-utils";
2
- import type { ServiceResponse } from "../remote-feature-flag-controller-types.cjs";
1
+ import type { PublicInterface } from "@metamask/utils";
2
+ import type { ClientConfigApiService } from "./client-config-api-service.cjs";
3
3
  /**
4
4
  * A service object responsible for fetching feature flags.
5
5
  */
6
- export type AbstractClientConfigApiService = Partial<Pick<ServicePolicy, 'onBreak' | 'onDegraded'>> & {
7
- /**
8
- * Fetches feature flags from the API with specific client, distribution, and
9
- * environment parameters. Provides structured error handling, including
10
- * fallback to cached data if available.
11
- *
12
- * @returns An object of feature flags and their boolean values or a
13
- * structured error object.
14
- */
15
- fetchRemoteFeatureFlags(): Promise<ServiceResponse>;
16
- };
6
+ export type AbstractClientConfigApiService = PublicInterface<ClientConfigApiService>;
17
7
  //# sourceMappingURL=abstract-client-config-api-service.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-client-config-api-service.d.cts","sourceRoot":"","sources":["../../src/client-config-api-service/abstract-client-config-api-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAEhE,OAAO,KAAK,EAAE,eAAe,EAAE,oDAAgD;AAE/E;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,OAAO,CAClD,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,YAAY,CAAC,CAC9C,GAAG;IACF;;;;;;;OAOG;IACH,uBAAuB,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;CACrD,CAAC"}
1
+ {"version":3,"file":"abstract-client-config-api-service.d.cts","sourceRoot":"","sources":["../../src/client-config-api-service/abstract-client-config-api-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,wBAAwB;AAEvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,wCAAoC;AAE1E;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACxC,eAAe,CAAC,sBAAsB,CAAC,CAAC"}
@@ -1,17 +1,7 @@
1
- import type { ServicePolicy } from "@metamask/controller-utils";
2
- import type { ServiceResponse } from "../remote-feature-flag-controller-types.mjs";
1
+ import type { PublicInterface } from "@metamask/utils";
2
+ import type { ClientConfigApiService } from "./client-config-api-service.mjs";
3
3
  /**
4
4
  * A service object responsible for fetching feature flags.
5
5
  */
6
- export type AbstractClientConfigApiService = Partial<Pick<ServicePolicy, 'onBreak' | 'onDegraded'>> & {
7
- /**
8
- * Fetches feature flags from the API with specific client, distribution, and
9
- * environment parameters. Provides structured error handling, including
10
- * fallback to cached data if available.
11
- *
12
- * @returns An object of feature flags and their boolean values or a
13
- * structured error object.
14
- */
15
- fetchRemoteFeatureFlags(): Promise<ServiceResponse>;
16
- };
6
+ export type AbstractClientConfigApiService = PublicInterface<ClientConfigApiService>;
17
7
  //# sourceMappingURL=abstract-client-config-api-service.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-client-config-api-service.d.mts","sourceRoot":"","sources":["../../src/client-config-api-service/abstract-client-config-api-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC;AAEhE,OAAO,KAAK,EAAE,eAAe,EAAE,oDAAgD;AAE/E;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,OAAO,CAClD,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,YAAY,CAAC,CAC9C,GAAG;IACF;;;;;;;OAOG;IACH,uBAAuB,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;CACrD,CAAC"}
1
+ {"version":3,"file":"abstract-client-config-api-service.d.mts","sourceRoot":"","sources":["../../src/client-config-api-service/abstract-client-config-api-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,wBAAwB;AAEvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,wCAAoC;AAE1E;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACxC,eAAe,CAAC,sBAAsB,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-client-config-api-service.mjs","sourceRoot":"","sources":["../../src/client-config-api-service/abstract-client-config-api-service.ts"],"names":[],"mappings":"","sourcesContent":["import type { ServicePolicy } from '@metamask/controller-utils';\n\nimport type { ServiceResponse } from '../remote-feature-flag-controller-types';\n\n/**\n * A service object responsible for fetching feature flags.\n */\nexport type AbstractClientConfigApiService = Partial<\n Pick<ServicePolicy, 'onBreak' | 'onDegraded'>\n> & {\n /**\n * Fetches feature flags from the API with specific client, distribution, and\n * environment parameters. Provides structured error handling, including\n * fallback to cached data if available.\n *\n * @returns An object of feature flags and their boolean values or a\n * structured error object.\n */\n fetchRemoteFeatureFlags(): Promise<ServiceResponse>;\n};\n"]}
1
+ {"version":3,"file":"abstract-client-config-api-service.mjs","sourceRoot":"","sources":["../../src/client-config-api-service/abstract-client-config-api-service.ts"],"names":[],"mappings":"","sourcesContent":["import type { PublicInterface } from '@metamask/utils';\n\nimport type { ClientConfigApiService } from './client-config-api-service';\n\n/**\n * A service object responsible for fetching feature flags.\n */\nexport type AbstractClientConfigApiService =\n PublicInterface<ClientConfigApiService>;\n"]}
@@ -12,14 +12,35 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
12
12
  };
13
13
  var _ClientConfigApiService_fetch, _ClientConfigApiService_policy, _ClientConfigApiService_client, _ClientConfigApiService_distribution, _ClientConfigApiService_environment;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.ClientConfigApiService = void 0;
16
- const controller_utils_1 = require("@metamask/controller-utils");
15
+ exports.ClientConfigApiService = exports.DEFAULT_DEGRADED_THRESHOLD = void 0;
16
+ const cockatiel_1 = require("cockatiel");
17
17
  const constants_1 = require("../constants.cjs");
18
+ const DEFAULT_FETCH_RETRIES = 3;
19
+ // Each update attempt will result (1 + retries) calls if the server is down
20
+ const DEFAULT_MAX_CONSECUTIVE_FAILURES = (1 + DEFAULT_FETCH_RETRIES) * 3;
21
+ exports.DEFAULT_DEGRADED_THRESHOLD = 5000;
18
22
  /**
19
23
  * This service is responsible for fetching feature flags from the ClientConfig API.
20
24
  */
21
25
  class ClientConfigApiService {
22
- 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, }) {
26
+ /**
27
+ * Constructs a new ClientConfigApiService object.
28
+ *
29
+ * @param args - The arguments.
30
+ * @param args.fetch - A function that can be used to make an HTTP request.
31
+ * @param args.retries - Number of retry attempts for each fetch request.
32
+ * @param args.maximumConsecutiveFailures - The maximum number of consecutive failures
33
+ * allowed before breaking the circuit and pausing further fetch attempts.
34
+ * @param args.circuitBreakDuration - The duration for which the circuit remains open after
35
+ * too many consecutive failures.
36
+ * @param args.onBreak - Callback invoked when the circuit breaks.
37
+ * @param args.onDegraded - Callback invoked when the service is degraded (requests resolving too slowly).
38
+ * @param args.config - The configuration object, includes client, distribution, and environment.
39
+ * @param args.config.client - The client type (e.g., 'extension', 'mobile').
40
+ * @param args.config.distribution - The distribution type (e.g., 'main', 'flask').
41
+ * @param args.config.environment - The environment type (e.g., 'prod', 'rc', 'dev').
42
+ */
43
+ constructor({ fetch: fetchFunction, retries = DEFAULT_FETCH_RETRIES, maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES, circuitBreakDuration = 30 * 60 * 1000, onBreak, onDegraded, config, }) {
23
44
  _ClientConfigApiService_fetch.set(this, void 0);
24
45
  _ClientConfigApiService_policy.set(this, void 0);
25
46
  _ClientConfigApiService_client.set(this, void 0);
@@ -29,37 +50,32 @@ class ClientConfigApiService {
29
50
  __classPrivateFieldSet(this, _ClientConfigApiService_client, config.client, "f");
30
51
  __classPrivateFieldSet(this, _ClientConfigApiService_distribution, config.distribution, "f");
31
52
  __classPrivateFieldSet(this, _ClientConfigApiService_environment, config.environment, "f");
32
- __classPrivateFieldSet(this, _ClientConfigApiService_policy, (0, controller_utils_1.createServicePolicy)({
33
- maxRetries: retries,
34
- maxConsecutiveFailures: maximumConsecutiveFailures,
35
- circuitBreakDuration,
36
- }), "f");
53
+ const retryPolicy = (0, cockatiel_1.retry)(cockatiel_1.handleAll, {
54
+ maxAttempts: retries,
55
+ backoff: new cockatiel_1.ExponentialBackoff(),
56
+ });
57
+ const circuitBreakerPolicy = (0, cockatiel_1.circuitBreaker)(cockatiel_1.handleAll, {
58
+ halfOpenAfter: circuitBreakDuration,
59
+ breaker: new cockatiel_1.ConsecutiveBreaker(maximumConsecutiveFailures),
60
+ });
37
61
  if (onBreak) {
38
- __classPrivateFieldGet(this, _ClientConfigApiService_policy, "f").onBreak(onBreak);
62
+ circuitBreakerPolicy.onBreak(onBreak);
39
63
  }
40
64
  if (onDegraded) {
41
- __classPrivateFieldGet(this, _ClientConfigApiService_policy, "f").onDegraded(onDegraded);
65
+ retryPolicy.onGiveUp(() => {
66
+ if (circuitBreakerPolicy.state === cockatiel_1.CircuitState.Closed) {
67
+ onDegraded();
68
+ }
69
+ });
70
+ retryPolicy.onSuccess(({ duration }) => {
71
+ if (circuitBreakerPolicy.state === cockatiel_1.CircuitState.Closed &&
72
+ duration > exports.DEFAULT_DEGRADED_THRESHOLD // Default degraded threshold
73
+ ) {
74
+ onDegraded();
75
+ }
76
+ });
42
77
  }
43
- }
44
- /**
45
- * Listens for when the request to the API fails too many times in a row.
46
- *
47
- * @param args - The same arguments that {@link ServicePolicy.onBreak}
48
- * takes.
49
- * @returns What {@link ServicePolicy.onBreak} returns.
50
- */
51
- onBreak(...args) {
52
- return __classPrivateFieldGet(this, _ClientConfigApiService_policy, "f").onBreak(...args);
53
- }
54
- /**
55
- * Listens for when the API is degraded.
56
- *
57
- * @param args - The same arguments that {@link ServicePolicy.onDegraded}
58
- * takes.
59
- * @returns What {@link ServicePolicy.onDegraded} returns.
60
- */
61
- onDegraded(...args) {
62
- return __classPrivateFieldGet(this, _ClientConfigApiService_policy, "f").onDegraded(...args);
78
+ __classPrivateFieldSet(this, _ClientConfigApiService_policy, (0, cockatiel_1.wrap)(retryPolicy, circuitBreakerPolicy), "f");
63
79
  }
64
80
  /**
65
81
  * Fetches feature flags from the API with specific client, distribution, and environment parameters.
@@ -1 +1 @@
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;QA3GD,gDAAqB;QAEZ,iDAAuB;QAEhC,iDAAoB;QAEpB,uDAAgC;QAEhC,sDAA8B;QAoG5B,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;YACX,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;SAC/B;QACD,IAAI,UAAU,EAAE;YACd,uBAAA,IAAI,sCAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;SACrC;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;;;;OAIG;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;YAChB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;SACzD;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACxB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;SAC9D;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;;;;;;;OAOG;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;AAlMD,wDAkMC","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 #fetch: typeof fetch;\n\n readonly #policy: ServicePolicy;\n\n #client: ClientType;\n\n #distribution: DistributionType;\n\n #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 * @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 * @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"]}
1
+ {"version":3,"file":"client-config-api-service.cjs","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n circuitBreaker,\n ConsecutiveBreaker,\n ExponentialBackoff,\n handleAll,\n type IPolicy,\n retry,\n wrap,\n CircuitState,\n} from 'cockatiel';\n\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\nconst DEFAULT_FETCH_RETRIES = 3;\n// Each update attempt will result (1 + retries) calls if the server is down\nconst DEFAULT_MAX_CONSECUTIVE_FAILURES = (1 + DEFAULT_FETCH_RETRIES) * 3;\n\nexport const DEFAULT_DEGRADED_THRESHOLD = 5000;\n\n/**\n * This service is responsible for fetching feature flags from the ClientConfig API.\n */\nexport class ClientConfigApiService {\n #fetch: typeof fetch;\n\n #policy: IPolicy;\n\n #client: ClientType;\n\n #distribution: DistributionType;\n\n #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 * @param args.retries - Number of retry attempts for each fetch request.\n * @param args.maximumConsecutiveFailures - The maximum number of consecutive failures\n * allowed before breaking the circuit and pausing further fetch attempts.\n * @param args.circuitBreakDuration - The duration for which the circuit remains open after\n * too many consecutive failures.\n * @param args.onBreak - Callback invoked when the circuit breaks.\n * @param args.onDegraded - Callback invoked when the service is degraded (requests resolving too slowly).\n * @param args.config - The configuration object, includes client, 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', 'flask').\n * @param args.config.environment - The environment type (e.g., 'prod', 'rc', 'dev').\n */\n constructor({\n fetch: fetchFunction,\n retries = DEFAULT_FETCH_RETRIES,\n maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES,\n circuitBreakDuration = 30 * 60 * 1000,\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 const retryPolicy = retry(handleAll, {\n maxAttempts: retries,\n backoff: new ExponentialBackoff(),\n });\n\n const circuitBreakerPolicy = circuitBreaker(handleAll, {\n halfOpenAfter: circuitBreakDuration,\n breaker: new ConsecutiveBreaker(maximumConsecutiveFailures),\n });\n\n if (onBreak) {\n circuitBreakerPolicy.onBreak(onBreak);\n }\n\n if (onDegraded) {\n retryPolicy.onGiveUp(() => {\n if (circuitBreakerPolicy.state === CircuitState.Closed) {\n onDegraded();\n }\n });\n\n retryPolicy.onSuccess(({ duration }) => {\n if (\n circuitBreakerPolicy.state === CircuitState.Closed &&\n duration > DEFAULT_DEGRADED_THRESHOLD // Default degraded threshold\n ) {\n onDegraded();\n }\n });\n }\n\n this.#policy = wrap(retryPolicy, circuitBreakerPolicy);\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 * @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 * @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"]}
@@ -1,73 +1,28 @@
1
- import type { ServicePolicy } from "@metamask/controller-utils";
2
- import type { AbstractClientConfigApiService } from "./abstract-client-config-api-service.cjs";
3
1
  import type { ClientType, DistributionType, EnvironmentType, ServiceResponse } from "../remote-feature-flag-controller-types.cjs";
2
+ export declare const DEFAULT_DEGRADED_THRESHOLD = 5000;
4
3
  /**
5
4
  * This service is responsible for fetching feature flags from the ClientConfig API.
6
5
  */
7
- export declare class ClientConfigApiService implements AbstractClientConfigApiService {
6
+ export declare class ClientConfigApiService {
8
7
  #private;
9
8
  /**
10
9
  * Constructs a new ClientConfigApiService object.
11
10
  *
12
11
  * @param args - The arguments.
13
12
  * @param args.fetch - A function that can be used to make an HTTP request.
14
- * If your JavaScript environment supports `fetch` natively, you'll probably
15
- * want to pass that; otherwise you can pass an equivalent (such as `fetch`
16
- * via `node-fetch`).
17
13
  * @param args.retries - Number of retry attempts for each fetch request.
18
- * @param args.maximumConsecutiveFailures - The maximum number of consecutive
19
- * failures allowed before breaking the circuit and pausing further fetch
20
- * attempts.
21
- * @param args.circuitBreakDuration - The amount of time to wait when the
22
- * circuit breaks from too many consecutive failures.
23
- * @param args.config - The configuration object, includes client,
24
- * distribution, and environment.
14
+ * @param args.maximumConsecutiveFailures - The maximum number of consecutive failures
15
+ * allowed before breaking the circuit and pausing further fetch attempts.
16
+ * @param args.circuitBreakDuration - The duration for which the circuit remains open after
17
+ * too many consecutive failures.
18
+ * @param args.onBreak - Callback invoked when the circuit breaks.
19
+ * @param args.onDegraded - Callback invoked when the service is degraded (requests resolving too slowly).
20
+ * @param args.config - The configuration object, includes client, distribution, and environment.
25
21
  * @param args.config.client - The client type (e.g., 'extension', 'mobile').
26
- * @param args.config.distribution - The distribution type (e.g., 'main',
27
- * 'flask').
28
- * @param args.config.environment - The environment type (e.g., 'prod', 'rc',
29
- * 'dev').
22
+ * @param args.config.distribution - The distribution type (e.g., 'main', 'flask').
23
+ * @param args.config.environment - The environment type (e.g., 'prod', 'rc', 'dev').
30
24
  */
31
- constructor(args: {
32
- fetch: typeof fetch;
33
- retries?: number;
34
- maximumConsecutiveFailures?: number;
35
- circuitBreakDuration?: number;
36
- config: {
37
- client: ClientType;
38
- distribution: DistributionType;
39
- environment: EnvironmentType;
40
- };
41
- });
42
- /**
43
- * Constructs a new ClientConfigApiService object.
44
- *
45
- * @deprecated This signature is deprecated; please use the `onBreak` and
46
- * `onDegraded` methods instead.
47
- * @param args - The arguments.
48
- * @param args.fetch - A function that can be used to make an HTTP request.
49
- * If your JavaScript environment supports `fetch` natively, you'll probably
50
- * want to pass that; otherwise you can pass an equivalent (such as `fetch`
51
- * via `node-fetch`).
52
- * @param args.retries - Number of retry attempts for each fetch request.
53
- * @param args.maximumConsecutiveFailures - The maximum number of consecutive
54
- * failures allowed before breaking the circuit and pausing further fetch
55
- * attempts.
56
- * @param args.circuitBreakDuration - The amount of time to wait when the
57
- * circuit breaks from too many consecutive failures.
58
- * @param args.onBreak - Callback for when the circuit breaks, useful
59
- * for capturing metrics about network failures.
60
- * @param args.onDegraded - Callback for when the API responds successfully
61
- * but takes too long to respond (5 seconds or more).
62
- * @param args.config - The configuration object, includes client,
63
- * distribution, and environment.
64
- * @param args.config.client - The client type (e.g., 'extension', 'mobile').
65
- * @param args.config.distribution - The distribution type (e.g., 'main',
66
- * 'flask').
67
- * @param args.config.environment - The environment type (e.g., 'prod', 'rc',
68
- * 'dev').
69
- */
70
- constructor(args: {
25
+ constructor({ fetch: fetchFunction, retries, maximumConsecutiveFailures, circuitBreakDuration, onBreak, onDegraded, config, }: {
71
26
  fetch: typeof fetch;
72
27
  retries?: number;
73
28
  maximumConsecutiveFailures?: number;
@@ -80,22 +35,6 @@ export declare class ClientConfigApiService implements AbstractClientConfigApiSe
80
35
  environment: EnvironmentType;
81
36
  };
82
37
  });
83
- /**
84
- * Listens for when the request to the API fails too many times in a row.
85
- *
86
- * @param args - The same arguments that {@link ServicePolicy.onBreak}
87
- * takes.
88
- * @returns What {@link ServicePolicy.onBreak} returns.
89
- */
90
- onBreak(...args: Parameters<ServicePolicy['onBreak']>): import("cockatiel").IDisposable;
91
- /**
92
- * Listens for when the API is degraded.
93
- *
94
- * @param args - The same arguments that {@link ServicePolicy.onDegraded}
95
- * takes.
96
- * @returns What {@link ServicePolicy.onDegraded} returns.
97
- */
98
- onDegraded(...args: Parameters<ServicePolicy['onDegraded']>): import("cockatiel").IDisposable;
99
38
  /**
100
39
  * Fetches feature flags from the API with specific client, distribution, and environment parameters.
101
40
  * Provides structured error handling, including fallback to cached data if available.
@@ -1 +1 @@
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;;;;OAIG;IACU,uBAAuB,IAAI,OAAO,CAAC,eAAe,CAAC;IA2BhE;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;CAK5B"}
1
+ {"version":3,"file":"client-config-api-service.d.cts","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAEV,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EAEhB,oDAAgD;AAMjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C;;GAEG;AACH,qBAAa,sBAAsB;;IAWjC;;;;;;;;;;;;;;;;OAgBG;gBACS,EACV,KAAK,EAAE,aAAa,EACpB,OAA+B,EAC/B,0BAA6D,EAC7D,oBAAqC,EACrC,OAAO,EACP,UAAU,EACV,MAAM,GACP,EAAE;QACD,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;IAwCD;;;;OAIG;IACU,uBAAuB,IAAI,OAAO,CAAC,eAAe,CAAC;IA2BhE;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;CAK5B"}
@@ -1,73 +1,28 @@
1
- import type { ServicePolicy } from "@metamask/controller-utils";
2
- import type { AbstractClientConfigApiService } from "./abstract-client-config-api-service.mjs";
3
1
  import type { ClientType, DistributionType, EnvironmentType, ServiceResponse } from "../remote-feature-flag-controller-types.mjs";
2
+ export declare const DEFAULT_DEGRADED_THRESHOLD = 5000;
4
3
  /**
5
4
  * This service is responsible for fetching feature flags from the ClientConfig API.
6
5
  */
7
- export declare class ClientConfigApiService implements AbstractClientConfigApiService {
6
+ export declare class ClientConfigApiService {
8
7
  #private;
9
8
  /**
10
9
  * Constructs a new ClientConfigApiService object.
11
10
  *
12
11
  * @param args - The arguments.
13
12
  * @param args.fetch - A function that can be used to make an HTTP request.
14
- * If your JavaScript environment supports `fetch` natively, you'll probably
15
- * want to pass that; otherwise you can pass an equivalent (such as `fetch`
16
- * via `node-fetch`).
17
13
  * @param args.retries - Number of retry attempts for each fetch request.
18
- * @param args.maximumConsecutiveFailures - The maximum number of consecutive
19
- * failures allowed before breaking the circuit and pausing further fetch
20
- * attempts.
21
- * @param args.circuitBreakDuration - The amount of time to wait when the
22
- * circuit breaks from too many consecutive failures.
23
- * @param args.config - The configuration object, includes client,
24
- * distribution, and environment.
14
+ * @param args.maximumConsecutiveFailures - The maximum number of consecutive failures
15
+ * allowed before breaking the circuit and pausing further fetch attempts.
16
+ * @param args.circuitBreakDuration - The duration for which the circuit remains open after
17
+ * too many consecutive failures.
18
+ * @param args.onBreak - Callback invoked when the circuit breaks.
19
+ * @param args.onDegraded - Callback invoked when the service is degraded (requests resolving too slowly).
20
+ * @param args.config - The configuration object, includes client, distribution, and environment.
25
21
  * @param args.config.client - The client type (e.g., 'extension', 'mobile').
26
- * @param args.config.distribution - The distribution type (e.g., 'main',
27
- * 'flask').
28
- * @param args.config.environment - The environment type (e.g., 'prod', 'rc',
29
- * 'dev').
22
+ * @param args.config.distribution - The distribution type (e.g., 'main', 'flask').
23
+ * @param args.config.environment - The environment type (e.g., 'prod', 'rc', 'dev').
30
24
  */
31
- constructor(args: {
32
- fetch: typeof fetch;
33
- retries?: number;
34
- maximumConsecutiveFailures?: number;
35
- circuitBreakDuration?: number;
36
- config: {
37
- client: ClientType;
38
- distribution: DistributionType;
39
- environment: EnvironmentType;
40
- };
41
- });
42
- /**
43
- * Constructs a new ClientConfigApiService object.
44
- *
45
- * @deprecated This signature is deprecated; please use the `onBreak` and
46
- * `onDegraded` methods instead.
47
- * @param args - The arguments.
48
- * @param args.fetch - A function that can be used to make an HTTP request.
49
- * If your JavaScript environment supports `fetch` natively, you'll probably
50
- * want to pass that; otherwise you can pass an equivalent (such as `fetch`
51
- * via `node-fetch`).
52
- * @param args.retries - Number of retry attempts for each fetch request.
53
- * @param args.maximumConsecutiveFailures - The maximum number of consecutive
54
- * failures allowed before breaking the circuit and pausing further fetch
55
- * attempts.
56
- * @param args.circuitBreakDuration - The amount of time to wait when the
57
- * circuit breaks from too many consecutive failures.
58
- * @param args.onBreak - Callback for when the circuit breaks, useful
59
- * for capturing metrics about network failures.
60
- * @param args.onDegraded - Callback for when the API responds successfully
61
- * but takes too long to respond (5 seconds or more).
62
- * @param args.config - The configuration object, includes client,
63
- * distribution, and environment.
64
- * @param args.config.client - The client type (e.g., 'extension', 'mobile').
65
- * @param args.config.distribution - The distribution type (e.g., 'main',
66
- * 'flask').
67
- * @param args.config.environment - The environment type (e.g., 'prod', 'rc',
68
- * 'dev').
69
- */
70
- constructor(args: {
25
+ constructor({ fetch: fetchFunction, retries, maximumConsecutiveFailures, circuitBreakDuration, onBreak, onDegraded, config, }: {
71
26
  fetch: typeof fetch;
72
27
  retries?: number;
73
28
  maximumConsecutiveFailures?: number;
@@ -80,22 +35,6 @@ export declare class ClientConfigApiService implements AbstractClientConfigApiSe
80
35
  environment: EnvironmentType;
81
36
  };
82
37
  });
83
- /**
84
- * Listens for when the request to the API fails too many times in a row.
85
- *
86
- * @param args - The same arguments that {@link ServicePolicy.onBreak}
87
- * takes.
88
- * @returns What {@link ServicePolicy.onBreak} returns.
89
- */
90
- onBreak(...args: Parameters<ServicePolicy['onBreak']>): import("cockatiel").IDisposable;
91
- /**
92
- * Listens for when the API is degraded.
93
- *
94
- * @param args - The same arguments that {@link ServicePolicy.onDegraded}
95
- * takes.
96
- * @returns What {@link ServicePolicy.onDegraded} returns.
97
- */
98
- onDegraded(...args: Parameters<ServicePolicy['onDegraded']>): import("cockatiel").IDisposable;
99
38
  /**
100
39
  * Fetches feature flags from the API with specific client, distribution, and environment parameters.
101
40
  * Provides structured error handling, including fallback to cached data if available.
@@ -1 +1 @@
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;;;;OAIG;IACU,uBAAuB,IAAI,OAAO,CAAC,eAAe,CAAC;IA2BhE;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;CAK5B"}
1
+ {"version":3,"file":"client-config-api-service.d.mts","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAEV,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EAEhB,oDAAgD;AAMjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C;;GAEG;AACH,qBAAa,sBAAsB;;IAWjC;;;;;;;;;;;;;;;;OAgBG;gBACS,EACV,KAAK,EAAE,aAAa,EACpB,OAA+B,EAC/B,0BAA6D,EAC7D,oBAAqC,EACrC,OAAO,EACP,UAAU,EACV,MAAM,GACP,EAAE;QACD,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;IAwCD;;;;OAIG;IACU,uBAAuB,IAAI,OAAO,CAAC,eAAe,CAAC;IA2BhE;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;CAK5B"}
@@ -10,13 +10,34 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
12
  var _ClientConfigApiService_fetch, _ClientConfigApiService_policy, _ClientConfigApiService_client, _ClientConfigApiService_distribution, _ClientConfigApiService_environment;
13
- import { createServicePolicy, DEFAULT_CIRCUIT_BREAK_DURATION, DEFAULT_MAX_CONSECUTIVE_FAILURES, DEFAULT_MAX_RETRIES } from "@metamask/controller-utils";
13
+ import { circuitBreaker, ConsecutiveBreaker, ExponentialBackoff, handleAll, retry, wrap, CircuitState } from "cockatiel";
14
14
  import { BASE_URL } from "../constants.mjs";
15
+ const DEFAULT_FETCH_RETRIES = 3;
16
+ // Each update attempt will result (1 + retries) calls if the server is down
17
+ const DEFAULT_MAX_CONSECUTIVE_FAILURES = (1 + DEFAULT_FETCH_RETRIES) * 3;
18
+ export const DEFAULT_DEGRADED_THRESHOLD = 5000;
15
19
  /**
16
20
  * This service is responsible for fetching feature flags from the ClientConfig API.
17
21
  */
18
22
  export class ClientConfigApiService {
19
- constructor({ fetch: fetchFunction, retries = DEFAULT_MAX_RETRIES, maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES, circuitBreakDuration = DEFAULT_CIRCUIT_BREAK_DURATION, onBreak, onDegraded, config, }) {
23
+ /**
24
+ * Constructs a new ClientConfigApiService object.
25
+ *
26
+ * @param args - The arguments.
27
+ * @param args.fetch - A function that can be used to make an HTTP request.
28
+ * @param args.retries - Number of retry attempts for each fetch request.
29
+ * @param args.maximumConsecutiveFailures - The maximum number of consecutive failures
30
+ * allowed before breaking the circuit and pausing further fetch attempts.
31
+ * @param args.circuitBreakDuration - The duration for which the circuit remains open after
32
+ * too many consecutive failures.
33
+ * @param args.onBreak - Callback invoked when the circuit breaks.
34
+ * @param args.onDegraded - Callback invoked when the service is degraded (requests resolving too slowly).
35
+ * @param args.config - The configuration object, includes client, distribution, and environment.
36
+ * @param args.config.client - The client type (e.g., 'extension', 'mobile').
37
+ * @param args.config.distribution - The distribution type (e.g., 'main', 'flask').
38
+ * @param args.config.environment - The environment type (e.g., 'prod', 'rc', 'dev').
39
+ */
40
+ constructor({ fetch: fetchFunction, retries = DEFAULT_FETCH_RETRIES, maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES, circuitBreakDuration = 30 * 60 * 1000, onBreak, onDegraded, config, }) {
20
41
  _ClientConfigApiService_fetch.set(this, void 0);
21
42
  _ClientConfigApiService_policy.set(this, void 0);
22
43
  _ClientConfigApiService_client.set(this, void 0);
@@ -26,37 +47,32 @@ export class ClientConfigApiService {
26
47
  __classPrivateFieldSet(this, _ClientConfigApiService_client, config.client, "f");
27
48
  __classPrivateFieldSet(this, _ClientConfigApiService_distribution, config.distribution, "f");
28
49
  __classPrivateFieldSet(this, _ClientConfigApiService_environment, config.environment, "f");
29
- __classPrivateFieldSet(this, _ClientConfigApiService_policy, createServicePolicy({
30
- maxRetries: retries,
31
- maxConsecutiveFailures: maximumConsecutiveFailures,
32
- circuitBreakDuration,
33
- }), "f");
50
+ const retryPolicy = retry(handleAll, {
51
+ maxAttempts: retries,
52
+ backoff: new ExponentialBackoff(),
53
+ });
54
+ const circuitBreakerPolicy = circuitBreaker(handleAll, {
55
+ halfOpenAfter: circuitBreakDuration,
56
+ breaker: new ConsecutiveBreaker(maximumConsecutiveFailures),
57
+ });
34
58
  if (onBreak) {
35
- __classPrivateFieldGet(this, _ClientConfigApiService_policy, "f").onBreak(onBreak);
59
+ circuitBreakerPolicy.onBreak(onBreak);
36
60
  }
37
61
  if (onDegraded) {
38
- __classPrivateFieldGet(this, _ClientConfigApiService_policy, "f").onDegraded(onDegraded);
62
+ retryPolicy.onGiveUp(() => {
63
+ if (circuitBreakerPolicy.state === CircuitState.Closed) {
64
+ onDegraded();
65
+ }
66
+ });
67
+ retryPolicy.onSuccess(({ duration }) => {
68
+ if (circuitBreakerPolicy.state === CircuitState.Closed &&
69
+ duration > DEFAULT_DEGRADED_THRESHOLD // Default degraded threshold
70
+ ) {
71
+ onDegraded();
72
+ }
73
+ });
39
74
  }
40
- }
41
- /**
42
- * Listens for when the request to the API fails too many times in a row.
43
- *
44
- * @param args - The same arguments that {@link ServicePolicy.onBreak}
45
- * takes.
46
- * @returns What {@link ServicePolicy.onBreak} returns.
47
- */
48
- onBreak(...args) {
49
- return __classPrivateFieldGet(this, _ClientConfigApiService_policy, "f").onBreak(...args);
50
- }
51
- /**
52
- * Listens for when the API is degraded.
53
- *
54
- * @param args - The same arguments that {@link ServicePolicy.onDegraded}
55
- * takes.
56
- * @returns What {@link ServicePolicy.onDegraded} returns.
57
- */
58
- onDegraded(...args) {
59
- return __classPrivateFieldGet(this, _ClientConfigApiService_policy, "f").onDegraded(...args);
75
+ __classPrivateFieldSet(this, _ClientConfigApiService_policy, wrap(retryPolicy, circuitBreakerPolicy), "f");
60
76
  }
61
77
  /**
62
78
  * Fetches feature flags from the API with specific client, distribution, and environment parameters.
@@ -1 +1 @@
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;QA3GD,gDAAqB;QAEZ,iDAAuB;QAEhC,iDAAoB;QAEpB,uDAAgC;QAEhC,sDAA8B;QAoG5B,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;YACX,uBAAA,IAAI,sCAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;SAC/B;QACD,IAAI,UAAU,EAAE;YACd,uBAAA,IAAI,sCAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;SACrC;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;;;;OAIG;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;YAChB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;SACzD;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACxB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;SAC9D;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;;;;;;;OAOG;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 #fetch: typeof fetch;\n\n readonly #policy: ServicePolicy;\n\n #client: ClientType;\n\n #distribution: DistributionType;\n\n #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 * @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 * @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"]}
1
+ {"version":3,"file":"client-config-api-service.mjs","sourceRoot":"","sources":["../../src/client-config-api-service/client-config-api-service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n circuitBreaker,\n ConsecutiveBreaker,\n ExponentialBackoff,\n handleAll,\n type IPolicy,\n retry,\n wrap,\n CircuitState,\n} from 'cockatiel';\n\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\nconst DEFAULT_FETCH_RETRIES = 3;\n// Each update attempt will result (1 + retries) calls if the server is down\nconst DEFAULT_MAX_CONSECUTIVE_FAILURES = (1 + DEFAULT_FETCH_RETRIES) * 3;\n\nexport const DEFAULT_DEGRADED_THRESHOLD = 5000;\n\n/**\n * This service is responsible for fetching feature flags from the ClientConfig API.\n */\nexport class ClientConfigApiService {\n #fetch: typeof fetch;\n\n #policy: IPolicy;\n\n #client: ClientType;\n\n #distribution: DistributionType;\n\n #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 * @param args.retries - Number of retry attempts for each fetch request.\n * @param args.maximumConsecutiveFailures - The maximum number of consecutive failures\n * allowed before breaking the circuit and pausing further fetch attempts.\n * @param args.circuitBreakDuration - The duration for which the circuit remains open after\n * too many consecutive failures.\n * @param args.onBreak - Callback invoked when the circuit breaks.\n * @param args.onDegraded - Callback invoked when the service is degraded (requests resolving too slowly).\n * @param args.config - The configuration object, includes client, 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', 'flask').\n * @param args.config.environment - The environment type (e.g., 'prod', 'rc', 'dev').\n */\n constructor({\n fetch: fetchFunction,\n retries = DEFAULT_FETCH_RETRIES,\n maximumConsecutiveFailures = DEFAULT_MAX_CONSECUTIVE_FAILURES,\n circuitBreakDuration = 30 * 60 * 1000,\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 const retryPolicy = retry(handleAll, {\n maxAttempts: retries,\n backoff: new ExponentialBackoff(),\n });\n\n const circuitBreakerPolicy = circuitBreaker(handleAll, {\n halfOpenAfter: circuitBreakDuration,\n breaker: new ConsecutiveBreaker(maximumConsecutiveFailures),\n });\n\n if (onBreak) {\n circuitBreakerPolicy.onBreak(onBreak);\n }\n\n if (onDegraded) {\n retryPolicy.onGiveUp(() => {\n if (circuitBreakerPolicy.state === CircuitState.Closed) {\n onDegraded();\n }\n });\n\n retryPolicy.onSuccess(({ duration }) => {\n if (\n circuitBreakerPolicy.state === CircuitState.Closed &&\n duration > DEFAULT_DEGRADED_THRESHOLD // Default degraded threshold\n ) {\n onDegraded();\n }\n });\n }\n\n this.#policy = wrap(retryPolicy, circuitBreakerPolicy);\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 * @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 * @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"]}
@@ -45,7 +45,7 @@ class RemoteFeatureFlagController extends base_controller_1.BaseController {
45
45
  * Constructs a new RemoteFeatureFlagController instance.
46
46
  *
47
47
  * @param options - The controller options.
48
- * @param options.messenger - The messenger used for communication.
48
+ * @param options.messenger - The controller messenger used for communication.
49
49
  * @param options.state - The initial state of the controller.
50
50
  * @param options.clientConfigApiService - The service instance to fetch remote feature flags.
51
51
  * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.
@@ -1 +1 @@
1
- {"version":3,"file":"remote-feature-flag-controller.cjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\n\nimport type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service';\nimport type {\n FeatureFlags,\n ServiceResponse,\n FeatureFlagScopeValue,\n} from './remote-feature-flag-controller-types';\nimport {\n generateDeterministicRandomNumber,\n isFeatureFlagWithScopeValue,\n} from './utils/user-segmentation-utils';\n\n// === GENERAL ===\n\nexport const controllerName = 'RemoteFeatureFlagController';\nexport const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day\n\n// === STATE ===\n\nexport type RemoteFeatureFlagControllerState = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: { persist: true, anonymous: true },\n cacheTimestamp: { persist: true, anonymous: true },\n};\n\n// === MESSENGER ===\n\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerGetRemoteFeatureFlagAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n RemoteFeatureFlagControllerGetStateAction;\n\nexport type AllowedActions = never;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type AllowedEvents = never;\n\nexport type RemoteFeatureFlagControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions | AllowedActions,\n RemoteFeatureFlagControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Returns the default state for the RemoteFeatureFlagController.\n *\n * @returns The default controller state.\n */\nexport function getDefaultRemoteFeatureFlagControllerState(): RemoteFeatureFlagControllerState {\n return {\n remoteFeatureFlags: {},\n cacheTimestamp: 0,\n };\n}\n\n/**\n * The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.\n * It fetches feature flags from a remote API, caches them, and provides methods to access\n * and manage these flags. The controller ensures that feature flags are refreshed based on\n * a specified interval and handles cases where the controller is disabled or the network is unavailable.\n */\nexport class RemoteFeatureFlagController extends BaseController<\n typeof controllerName,\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger\n> {\n readonly #fetchInterval: number;\n\n #disabled: boolean;\n\n readonly #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n #getMetaMetricsId: () => string;\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 */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId: () => string;\n fetchInterval?: number;\n disabled?: boolean;\n }) {\n super({\n name: controllerName,\n metadata: remoteFeatureFlagControllerMetadata,\n messenger,\n state: {\n ...getDefaultRemoteFeatureFlagControllerState(),\n ...state,\n },\n });\n\n this.#fetchInterval = fetchInterval;\n this.#disabled = disabled;\n this.#clientConfigApiService = clientConfigApiService;\n this.#getMetaMetricsId = getMetaMetricsId;\n }\n\n /**\n * Checks if the cached feature flags are expired based on the fetch interval.\n *\n * @returns Whether the cache is expired (`true`) or still valid (`false`).\n * @private\n */\n #isCacheExpired(): boolean {\n return Date.now() - this.state.cacheTimestamp > this.#fetchInterval;\n }\n\n /**\n * Retrieves the remote feature flags, fetching from the API if necessary.\n * Uses caching to prevent redundant API calls and handles concurrent fetches.\n *\n * @returns A promise that resolves to the current set of feature flags.\n */\n async updateRemoteFeatureFlags(): Promise<void> {\n if (this.#disabled || !this.#isCacheExpired()) {\n return;\n }\n\n let serverData;\n\n if (this.#inProgressFlagUpdate) {\n await this.#inProgressFlagUpdate;\n return;\n }\n\n try {\n this.#inProgressFlagUpdate =\n this.#clientConfigApiService.fetchRemoteFeatureFlags();\n\n serverData = await this.#inProgressFlagUpdate;\n } finally {\n this.#inProgressFlagUpdate = undefined;\n }\n\n await this.#updateCache(serverData.remoteFeatureFlags);\n }\n\n /**\n * Updates the controller's state with new feature flags and resets the cache timestamp.\n *\n * @param remoteFeatureFlags - The new feature flags to cache.\n * @private\n */\n async #updateCache(remoteFeatureFlags: FeatureFlags) {\n const processedRemoteFeatureFlags = await this.#processRemoteFeatureFlags(remoteFeatureFlags);\n this.update(() => {\n return {\n remoteFeatureFlags: processedRemoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n async #processRemoteFeatureFlags(\n remoteFeatureFlags: FeatureFlags,\n ): Promise<FeatureFlags> {\n const processedRemoteFeatureFlags: FeatureFlags = {};\n const metaMetricsId = this.#getMetaMetricsId();\n const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = remoteFeatureFlagValue;\n\n if (Array.isArray(remoteFeatureFlagValue) && thresholdValue) {\n const selectedGroup = remoteFeatureFlagValue.find(\n (featureFlag): featureFlag is FeatureFlagScopeValue => {\n if (!isFeatureFlagWithScopeValue(featureFlag)) {\n return false;\n }\n\n return thresholdValue <= featureFlag.scope.value;\n },\n );\n if (selectedGroup) {\n processedValue = {\n name: selectedGroup.name,\n value: selectedGroup.value,\n };\n }\n }\n\n processedRemoteFeatureFlags[remoteFeatureFlagName] = processedValue;\n }\n return processedRemoteFeatureFlags;\n }\n\n /**\n * Enables the controller, allowing it to make network requests.\n */\n enable(): void {\n this.#disabled = false;\n }\n\n /**\n * Disables the controller, preventing it from making network requests.\n */\n disable(): void {\n this.#disabled = true;\n }\n}\n"]}
1
+ {"version":3,"file":"remote-feature-flag-controller.cjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\n\nimport type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service';\nimport type {\n FeatureFlags,\n ServiceResponse,\n FeatureFlagScopeValue,\n} from './remote-feature-flag-controller-types';\nimport {\n generateDeterministicRandomNumber,\n isFeatureFlagWithScopeValue,\n} from './utils/user-segmentation-utils';\n\n// === GENERAL ===\n\nexport const controllerName = 'RemoteFeatureFlagController';\nexport const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day\n\n// === STATE ===\n\nexport type RemoteFeatureFlagControllerState = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: { persist: true, anonymous: true },\n cacheTimestamp: { persist: true, anonymous: true },\n};\n\n// === MESSENGER ===\n\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerGetRemoteFeatureFlagAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n RemoteFeatureFlagControllerGetStateAction;\n\nexport type AllowedActions = never;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type AllowedEvents = never;\n\nexport type RemoteFeatureFlagControllerMessenger =\n RestrictedControllerMessenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions | AllowedActions,\n RemoteFeatureFlagControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n >;\n\n/**\n * Returns the default state for the RemoteFeatureFlagController.\n *\n * @returns The default controller state.\n */\nexport function getDefaultRemoteFeatureFlagControllerState(): RemoteFeatureFlagControllerState {\n return {\n remoteFeatureFlags: {},\n cacheTimestamp: 0,\n };\n}\n\n/**\n * The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.\n * It fetches feature flags from a remote API, caches them, and provides methods to access\n * and manage these flags. The controller ensures that feature flags are refreshed based on\n * a specified interval and handles cases where the controller is disabled or the network is unavailable.\n */\nexport class RemoteFeatureFlagController extends BaseController<\n typeof controllerName,\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger\n> {\n readonly #fetchInterval: number;\n\n #disabled: boolean;\n\n #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n #getMetaMetricsId: () => string;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The controller messenger used for communication.\n * @param options.state - The initial state of the controller.\n * @param options.clientConfigApiService - The service instance to fetch remote feature flags.\n * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.\n * @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.\n * @param options.getMetaMetricsId - Returns metaMetricsId.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId: () => string;\n fetchInterval?: number;\n disabled?: boolean;\n }) {\n super({\n name: controllerName,\n metadata: remoteFeatureFlagControllerMetadata,\n messenger,\n state: {\n ...getDefaultRemoteFeatureFlagControllerState(),\n ...state,\n },\n });\n\n this.#fetchInterval = fetchInterval;\n this.#disabled = disabled;\n this.#clientConfigApiService = clientConfigApiService;\n this.#getMetaMetricsId = getMetaMetricsId;\n }\n\n /**\n * Checks if the cached feature flags are expired based on the fetch interval.\n *\n * @returns Whether the cache is expired (`true`) or still valid (`false`).\n * @private\n */\n #isCacheExpired(): boolean {\n return Date.now() - this.state.cacheTimestamp > this.#fetchInterval;\n }\n\n /**\n * Retrieves the remote feature flags, fetching from the API if necessary.\n * Uses caching to prevent redundant API calls and handles concurrent fetches.\n *\n * @returns A promise that resolves to the current set of feature flags.\n */\n async updateRemoteFeatureFlags(): Promise<void> {\n if (this.#disabled || !this.#isCacheExpired()) {\n return;\n }\n\n let serverData;\n\n if (this.#inProgressFlagUpdate) {\n await this.#inProgressFlagUpdate;\n return;\n }\n\n try {\n this.#inProgressFlagUpdate =\n this.#clientConfigApiService.fetchRemoteFeatureFlags();\n\n serverData = await this.#inProgressFlagUpdate;\n } finally {\n this.#inProgressFlagUpdate = undefined;\n }\n\n await this.#updateCache(serverData.remoteFeatureFlags);\n }\n\n /**\n * Updates the controller's state with new feature flags and resets the cache timestamp.\n *\n * @param remoteFeatureFlags - The new feature flags to cache.\n * @private\n */\n async #updateCache(remoteFeatureFlags: FeatureFlags) {\n const processedRemoteFeatureFlags = await this.#processRemoteFeatureFlags(\n remoteFeatureFlags,\n );\n this.update(() => {\n return {\n remoteFeatureFlags: processedRemoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n async #processRemoteFeatureFlags(\n remoteFeatureFlags: FeatureFlags,\n ): Promise<FeatureFlags> {\n const processedRemoteFeatureFlags: FeatureFlags = {};\n const metaMetricsId = this.#getMetaMetricsId();\n const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = remoteFeatureFlagValue;\n\n if (Array.isArray(remoteFeatureFlagValue) && thresholdValue) {\n const selectedGroup = remoteFeatureFlagValue.find(\n (featureFlag): featureFlag is FeatureFlagScopeValue => {\n if (!isFeatureFlagWithScopeValue(featureFlag)) {\n return false;\n }\n\n return thresholdValue <= featureFlag.scope.value;\n },\n );\n if (selectedGroup) {\n processedValue = {\n name: selectedGroup.name,\n value: selectedGroup.value,\n };\n }\n }\n\n processedRemoteFeatureFlags[remoteFeatureFlagName] = processedValue;\n }\n return processedRemoteFeatureFlags;\n }\n\n /**\n * Enables the controller, allowing it to make network requests.\n */\n enable(): void {\n this.#disabled = false;\n }\n\n /**\n * Disables the controller, preventing it from making network requests.\n */\n disable(): void {\n this.#disabled = true;\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { ControllerGetStateAction, ControllerStateChangeEvent, RestrictedMessenger } from "@metamask/base-controller";
1
+ import type { ControllerGetStateAction, ControllerStateChangeEvent, RestrictedControllerMessenger } from "@metamask/base-controller";
2
2
  import { BaseController } from "@metamask/base-controller";
3
3
  import type { AbstractClientConfigApiService } from "./client-config-api-service/abstract-client-config-api-service.cjs";
4
4
  import type { FeatureFlags } from "./remote-feature-flag-controller-types.cjs";
@@ -18,7 +18,7 @@ export type AllowedActions = never;
18
18
  export type RemoteFeatureFlagControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, RemoteFeatureFlagControllerState>;
19
19
  export type RemoteFeatureFlagControllerEvents = RemoteFeatureFlagControllerStateChangeEvent;
20
20
  export type AllowedEvents = never;
21
- export type RemoteFeatureFlagControllerMessenger = RestrictedMessenger<typeof controllerName, RemoteFeatureFlagControllerActions | AllowedActions, RemoteFeatureFlagControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
21
+ export type RemoteFeatureFlagControllerMessenger = RestrictedControllerMessenger<typeof controllerName, RemoteFeatureFlagControllerActions | AllowedActions, RemoteFeatureFlagControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
22
22
  /**
23
23
  * Returns the default state for the RemoteFeatureFlagController.
24
24
  *
@@ -37,7 +37,7 @@ export declare class RemoteFeatureFlagController extends BaseController<typeof c
37
37
  * Constructs a new RemoteFeatureFlagController instance.
38
38
  *
39
39
  * @param options - The controller options.
40
- * @param options.messenger - The messenger used for communication.
40
+ * @param options.messenger - The controller messenger used for communication.
41
41
  * @param options.state - The initial state of the controller.
42
42
  * @param options.clientConfigApiService - The service instance to fetch remote feature flags.
43
43
  * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.
@@ -1 +1 @@
1
- {"version":3,"file":"remote-feature-flag-controller.d.cts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EACpB,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAAuE;AACrH,OAAO,KAAK,EACV,YAAY,EAGb,mDAA+C;AAQhD,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAC5D,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAI1D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AASF,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,qDAAqD,GAAG;IAClE,IAAI,EAAE,GAAG,OAAO,cAAc,2BAA2B,CAAC;IAC1D,OAAO,EAAE,2BAA2B,CAAC,0BAA0B,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAC5C,yCAAyC,CAAC;AAE5C,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC;AAEnC,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC;AAElC,MAAM,MAAM,oCAAoC,GAAG,mBAAmB,CACpE,OAAO,cAAc,EACrB,kCAAkC,GAAG,cAAc,EACnD,iCAAiC,GAAG,aAAa,EACjD,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAK7F;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAWC;;;;;;;;;;OAUG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,GACjB,EAAE;QACD,SAAS,EAAE,oCAAoC,CAAC;QAChD,KAAK,CAAC,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAClD,sBAAsB,EAAE,8BAA8B,CAAC;QACvD,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;IA2BD;;;;;OAKG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IA4E/C;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
1
+ {"version":3,"file":"remote-feature-flag-controller.d.cts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,6BAA6B,EAC9B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAAuE;AACrH,OAAO,KAAK,EACV,YAAY,EAGb,mDAA+C;AAQhD,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAC5D,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAI1D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AASF,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,qDAAqD,GAAG;IAClE,IAAI,EAAE,GAAG,OAAO,cAAc,2BAA2B,CAAC;IAC1D,OAAO,EAAE,2BAA2B,CAAC,0BAA0B,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAC5C,yCAAyC,CAAC;AAE5C,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC;AAEnC,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC;AAElC,MAAM,MAAM,oCAAoC,GAC9C,6BAA6B,CAC3B,OAAO,cAAc,EACrB,kCAAkC,GAAG,cAAc,EACnD,iCAAiC,GAAG,aAAa,EACjD,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEJ;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAK7F;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAWC;;;;;;;;;;OAUG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,GACjB,EAAE;QACD,SAAS,EAAE,oCAAoC,CAAC;QAChD,KAAK,CAAC,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAClD,sBAAsB,EAAE,8BAA8B,CAAC;QACvD,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;IA2BD;;;;;OAKG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IA8E/C;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
@@ -1,4 +1,4 @@
1
- import type { ControllerGetStateAction, ControllerStateChangeEvent, RestrictedMessenger } from "@metamask/base-controller";
1
+ import type { ControllerGetStateAction, ControllerStateChangeEvent, RestrictedControllerMessenger } from "@metamask/base-controller";
2
2
  import { BaseController } from "@metamask/base-controller";
3
3
  import type { AbstractClientConfigApiService } from "./client-config-api-service/abstract-client-config-api-service.mjs";
4
4
  import type { FeatureFlags } from "./remote-feature-flag-controller-types.mjs";
@@ -18,7 +18,7 @@ export type AllowedActions = never;
18
18
  export type RemoteFeatureFlagControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, RemoteFeatureFlagControllerState>;
19
19
  export type RemoteFeatureFlagControllerEvents = RemoteFeatureFlagControllerStateChangeEvent;
20
20
  export type AllowedEvents = never;
21
- export type RemoteFeatureFlagControllerMessenger = RestrictedMessenger<typeof controllerName, RemoteFeatureFlagControllerActions | AllowedActions, RemoteFeatureFlagControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
21
+ export type RemoteFeatureFlagControllerMessenger = RestrictedControllerMessenger<typeof controllerName, RemoteFeatureFlagControllerActions | AllowedActions, RemoteFeatureFlagControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
22
22
  /**
23
23
  * Returns the default state for the RemoteFeatureFlagController.
24
24
  *
@@ -37,7 +37,7 @@ export declare class RemoteFeatureFlagController extends BaseController<typeof c
37
37
  * Constructs a new RemoteFeatureFlagController instance.
38
38
  *
39
39
  * @param options - The controller options.
40
- * @param options.messenger - The messenger used for communication.
40
+ * @param options.messenger - The controller messenger used for communication.
41
41
  * @param options.state - The initial state of the controller.
42
42
  * @param options.clientConfigApiService - The service instance to fetch remote feature flags.
43
43
  * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.
@@ -1 +1 @@
1
- {"version":3,"file":"remote-feature-flag-controller.d.mts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EACpB,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAAuE;AACrH,OAAO,KAAK,EACV,YAAY,EAGb,mDAA+C;AAQhD,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAC5D,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAI1D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AASF,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,qDAAqD,GAAG;IAClE,IAAI,EAAE,GAAG,OAAO,cAAc,2BAA2B,CAAC;IAC1D,OAAO,EAAE,2BAA2B,CAAC,0BAA0B,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAC5C,yCAAyC,CAAC;AAE5C,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC;AAEnC,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC;AAElC,MAAM,MAAM,oCAAoC,GAAG,mBAAmB,CACpE,OAAO,cAAc,EACrB,kCAAkC,GAAG,cAAc,EACnD,iCAAiC,GAAG,aAAa,EACjD,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAK7F;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAWC;;;;;;;;;;OAUG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,GACjB,EAAE;QACD,SAAS,EAAE,oCAAoC,CAAC;QAChD,KAAK,CAAC,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAClD,sBAAsB,EAAE,8BAA8B,CAAC;QACvD,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;IA2BD;;;;;OAKG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IA4E/C;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
1
+ {"version":3,"file":"remote-feature-flag-controller.d.mts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,6BAA6B,EAC9B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAAuE;AACrH,OAAO,KAAK,EACV,YAAY,EAGb,mDAA+C;AAQhD,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAC5D,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAI1D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AASF,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,qDAAqD,GAAG;IAClE,IAAI,EAAE,GAAG,OAAO,cAAc,2BAA2B,CAAC;IAC1D,OAAO,EAAE,2BAA2B,CAAC,0BAA0B,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAC5C,yCAAyC,CAAC;AAE5C,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC;AAEnC,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC;AAElC,MAAM,MAAM,oCAAoC,GAC9C,6BAA6B,CAC3B,OAAO,cAAc,EACrB,kCAAkC,GAAG,cAAc,EACnD,iCAAiC,GAAG,aAAa,EACjD,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEJ;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAK7F;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAWC;;;;;;;;;;OAUG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,GACjB,EAAE;QACD,SAAS,EAAE,oCAAoC,CAAC;QAChD,KAAK,CAAC,EAAE,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAClD,sBAAsB,EAAE,8BAA8B,CAAC;QACvD,gBAAgB,EAAE,MAAM,MAAM,CAAC;QAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;IA2BD;;;;;OAKG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IA8E/C;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
@@ -41,7 +41,7 @@ export class RemoteFeatureFlagController extends BaseController {
41
41
  * Constructs a new RemoteFeatureFlagController instance.
42
42
  *
43
43
  * @param options - The controller options.
44
- * @param options.messenger - The messenger used for communication.
44
+ * @param options.messenger - The controller messenger used for communication.
45
45
  * @param options.state - The initial state of the controller.
46
46
  * @param options.clientConfigApiService - The service instance to fetch remote feature flags.
47
47
  * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.
@@ -1 +1 @@
1
- {"version":3,"file":"remote-feature-flag-controller.mjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\n\nimport type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service';\nimport type {\n FeatureFlags,\n ServiceResponse,\n FeatureFlagScopeValue,\n} from './remote-feature-flag-controller-types';\nimport {\n generateDeterministicRandomNumber,\n isFeatureFlagWithScopeValue,\n} from './utils/user-segmentation-utils';\n\n// === GENERAL ===\n\nexport const controllerName = 'RemoteFeatureFlagController';\nexport const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day\n\n// === STATE ===\n\nexport type RemoteFeatureFlagControllerState = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: { persist: true, anonymous: true },\n cacheTimestamp: { persist: true, anonymous: true },\n};\n\n// === MESSENGER ===\n\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerGetRemoteFeatureFlagAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n RemoteFeatureFlagControllerGetStateAction;\n\nexport type AllowedActions = never;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type AllowedEvents = never;\n\nexport type RemoteFeatureFlagControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions | AllowedActions,\n RemoteFeatureFlagControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Returns the default state for the RemoteFeatureFlagController.\n *\n * @returns The default controller state.\n */\nexport function getDefaultRemoteFeatureFlagControllerState(): RemoteFeatureFlagControllerState {\n return {\n remoteFeatureFlags: {},\n cacheTimestamp: 0,\n };\n}\n\n/**\n * The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.\n * It fetches feature flags from a remote API, caches them, and provides methods to access\n * and manage these flags. The controller ensures that feature flags are refreshed based on\n * a specified interval and handles cases where the controller is disabled or the network is unavailable.\n */\nexport class RemoteFeatureFlagController extends BaseController<\n typeof controllerName,\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger\n> {\n readonly #fetchInterval: number;\n\n #disabled: boolean;\n\n readonly #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n #getMetaMetricsId: () => string;\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 */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId: () => string;\n fetchInterval?: number;\n disabled?: boolean;\n }) {\n super({\n name: controllerName,\n metadata: remoteFeatureFlagControllerMetadata,\n messenger,\n state: {\n ...getDefaultRemoteFeatureFlagControllerState(),\n ...state,\n },\n });\n\n this.#fetchInterval = fetchInterval;\n this.#disabled = disabled;\n this.#clientConfigApiService = clientConfigApiService;\n this.#getMetaMetricsId = getMetaMetricsId;\n }\n\n /**\n * Checks if the cached feature flags are expired based on the fetch interval.\n *\n * @returns Whether the cache is expired (`true`) or still valid (`false`).\n * @private\n */\n #isCacheExpired(): boolean {\n return Date.now() - this.state.cacheTimestamp > this.#fetchInterval;\n }\n\n /**\n * Retrieves the remote feature flags, fetching from the API if necessary.\n * Uses caching to prevent redundant API calls and handles concurrent fetches.\n *\n * @returns A promise that resolves to the current set of feature flags.\n */\n async updateRemoteFeatureFlags(): Promise<void> {\n if (this.#disabled || !this.#isCacheExpired()) {\n return;\n }\n\n let serverData;\n\n if (this.#inProgressFlagUpdate) {\n await this.#inProgressFlagUpdate;\n return;\n }\n\n try {\n this.#inProgressFlagUpdate =\n this.#clientConfigApiService.fetchRemoteFeatureFlags();\n\n serverData = await this.#inProgressFlagUpdate;\n } finally {\n this.#inProgressFlagUpdate = undefined;\n }\n\n await this.#updateCache(serverData.remoteFeatureFlags);\n }\n\n /**\n * Updates the controller's state with new feature flags and resets the cache timestamp.\n *\n * @param remoteFeatureFlags - The new feature flags to cache.\n * @private\n */\n async #updateCache(remoteFeatureFlags: FeatureFlags) {\n const processedRemoteFeatureFlags = await this.#processRemoteFeatureFlags(remoteFeatureFlags);\n this.update(() => {\n return {\n remoteFeatureFlags: processedRemoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n async #processRemoteFeatureFlags(\n remoteFeatureFlags: FeatureFlags,\n ): Promise<FeatureFlags> {\n const processedRemoteFeatureFlags: FeatureFlags = {};\n const metaMetricsId = this.#getMetaMetricsId();\n const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = remoteFeatureFlagValue;\n\n if (Array.isArray(remoteFeatureFlagValue) && thresholdValue) {\n const selectedGroup = remoteFeatureFlagValue.find(\n (featureFlag): featureFlag is FeatureFlagScopeValue => {\n if (!isFeatureFlagWithScopeValue(featureFlag)) {\n return false;\n }\n\n return thresholdValue <= featureFlag.scope.value;\n },\n );\n if (selectedGroup) {\n processedValue = {\n name: selectedGroup.name,\n value: selectedGroup.value,\n };\n }\n }\n\n processedRemoteFeatureFlags[remoteFeatureFlagName] = processedValue;\n }\n return processedRemoteFeatureFlags;\n }\n\n /**\n * Enables the controller, allowing it to make network requests.\n */\n enable(): void {\n this.#disabled = false;\n }\n\n /**\n * Disables the controller, preventing it from making network requests.\n */\n disable(): void {\n this.#disabled = true;\n }\n}\n"]}
1
+ {"version":3,"file":"remote-feature-flag-controller.mjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\n\nimport type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service';\nimport type {\n FeatureFlags,\n ServiceResponse,\n FeatureFlagScopeValue,\n} from './remote-feature-flag-controller-types';\nimport {\n generateDeterministicRandomNumber,\n isFeatureFlagWithScopeValue,\n} from './utils/user-segmentation-utils';\n\n// === GENERAL ===\n\nexport const controllerName = 'RemoteFeatureFlagController';\nexport const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day\n\n// === STATE ===\n\nexport type RemoteFeatureFlagControllerState = {\n remoteFeatureFlags: FeatureFlags;\n cacheTimestamp: number;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: { persist: true, anonymous: true },\n cacheTimestamp: { persist: true, anonymous: true },\n};\n\n// === MESSENGER ===\n\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerGetRemoteFeatureFlagAction = {\n type: `${typeof controllerName}:updateRemoteFeatureFlags`;\n handler: RemoteFeatureFlagController['updateRemoteFeatureFlags'];\n};\n\nexport type RemoteFeatureFlagControllerActions =\n RemoteFeatureFlagControllerGetStateAction;\n\nexport type AllowedActions = never;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type AllowedEvents = never;\n\nexport type RemoteFeatureFlagControllerMessenger =\n RestrictedControllerMessenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions | AllowedActions,\n RemoteFeatureFlagControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n >;\n\n/**\n * Returns the default state for the RemoteFeatureFlagController.\n *\n * @returns The default controller state.\n */\nexport function getDefaultRemoteFeatureFlagControllerState(): RemoteFeatureFlagControllerState {\n return {\n remoteFeatureFlags: {},\n cacheTimestamp: 0,\n };\n}\n\n/**\n * The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.\n * It fetches feature flags from a remote API, caches them, and provides methods to access\n * and manage these flags. The controller ensures that feature flags are refreshed based on\n * a specified interval and handles cases where the controller is disabled or the network is unavailable.\n */\nexport class RemoteFeatureFlagController extends BaseController<\n typeof controllerName,\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger\n> {\n readonly #fetchInterval: number;\n\n #disabled: boolean;\n\n #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n #getMetaMetricsId: () => string;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The controller messenger used for communication.\n * @param options.state - The initial state of the controller.\n * @param options.clientConfigApiService - The service instance to fetch remote feature flags.\n * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.\n * @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.\n * @param options.getMetaMetricsId - Returns metaMetricsId.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId: () => string;\n fetchInterval?: number;\n disabled?: boolean;\n }) {\n super({\n name: controllerName,\n metadata: remoteFeatureFlagControllerMetadata,\n messenger,\n state: {\n ...getDefaultRemoteFeatureFlagControllerState(),\n ...state,\n },\n });\n\n this.#fetchInterval = fetchInterval;\n this.#disabled = disabled;\n this.#clientConfigApiService = clientConfigApiService;\n this.#getMetaMetricsId = getMetaMetricsId;\n }\n\n /**\n * Checks if the cached feature flags are expired based on the fetch interval.\n *\n * @returns Whether the cache is expired (`true`) or still valid (`false`).\n * @private\n */\n #isCacheExpired(): boolean {\n return Date.now() - this.state.cacheTimestamp > this.#fetchInterval;\n }\n\n /**\n * Retrieves the remote feature flags, fetching from the API if necessary.\n * Uses caching to prevent redundant API calls and handles concurrent fetches.\n *\n * @returns A promise that resolves to the current set of feature flags.\n */\n async updateRemoteFeatureFlags(): Promise<void> {\n if (this.#disabled || !this.#isCacheExpired()) {\n return;\n }\n\n let serverData;\n\n if (this.#inProgressFlagUpdate) {\n await this.#inProgressFlagUpdate;\n return;\n }\n\n try {\n this.#inProgressFlagUpdate =\n this.#clientConfigApiService.fetchRemoteFeatureFlags();\n\n serverData = await this.#inProgressFlagUpdate;\n } finally {\n this.#inProgressFlagUpdate = undefined;\n }\n\n await this.#updateCache(serverData.remoteFeatureFlags);\n }\n\n /**\n * Updates the controller's state with new feature flags and resets the cache timestamp.\n *\n * @param remoteFeatureFlags - The new feature flags to cache.\n * @private\n */\n async #updateCache(remoteFeatureFlags: FeatureFlags) {\n const processedRemoteFeatureFlags = await this.#processRemoteFeatureFlags(\n remoteFeatureFlags,\n );\n this.update(() => {\n return {\n remoteFeatureFlags: processedRemoteFeatureFlags,\n cacheTimestamp: Date.now(),\n };\n });\n }\n\n async #processRemoteFeatureFlags(\n remoteFeatureFlags: FeatureFlags,\n ): Promise<FeatureFlags> {\n const processedRemoteFeatureFlags: FeatureFlags = {};\n const metaMetricsId = this.#getMetaMetricsId();\n const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = remoteFeatureFlagValue;\n\n if (Array.isArray(remoteFeatureFlagValue) && thresholdValue) {\n const selectedGroup = remoteFeatureFlagValue.find(\n (featureFlag): featureFlag is FeatureFlagScopeValue => {\n if (!isFeatureFlagWithScopeValue(featureFlag)) {\n return false;\n }\n\n return thresholdValue <= featureFlag.scope.value;\n },\n );\n if (selectedGroup) {\n processedValue = {\n name: selectedGroup.name,\n value: selectedGroup.value,\n };\n }\n }\n\n processedRemoteFeatureFlags[remoteFeatureFlagName] = processedValue;\n }\n return processedRemoteFeatureFlags;\n }\n\n /**\n * Enables the controller, allowing it to make network requests.\n */\n enable(): void {\n this.#disabled = false;\n }\n\n /**\n * Disables the controller, preventing it from making network requests.\n */\n disable(): void {\n this.#disabled = true;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/remote-feature-flag-controller",
3
- "version": "1.3.0-preview-59b4e66e",
3
+ "version": "1.3.0-preview-4c4eb52a",
4
4
  "description": "The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags",
5
5
  "keywords": [
6
6
  "MetaMask",
@@ -48,13 +48,14 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@metamask/base-controller": "^7.1.1",
51
- "@metamask/controller-utils": "^11.5.0",
52
- "@metamask/utils": "^11.1.0",
51
+ "@metamask/utils": "^11.0.1",
52
+ "cockatiel": "^3.1.2",
53
53
  "uuid": "^8.3.2"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@lavamoat/allow-scripts": "^3.0.4",
57
57
  "@metamask/auto-changelog": "^3.4.4",
58
+ "@metamask/controller-utils": "^11.4.5",
58
59
  "@types/jest": "^27.4.1",
59
60
  "deepmerge": "^4.2.2",
60
61
  "jest": "^27.5.1",