@metamask-previews/remote-feature-flag-controller 6.1.0-preview-8c2a2eeb2 → 6.1.0-preview-4f4c98e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/dist/client-config-api-service/{abstract-client-config-api-service.d.mts → abstract-client-config-api-service.d.ts} +3 -3
- package/dist/client-config-api-service/abstract-client-config-api-service.d.ts.map +1 -0
- package/dist/client-config-api-service/abstract-client-config-api-service.js +2 -0
- package/dist/client-config-api-service/abstract-client-config-api-service.js.map +1 -0
- package/dist/client-config-api-service/{client-config-api-service.d.cts → client-config-api-service.d.ts} +5 -5
- package/dist/client-config-api-service/client-config-api-service.d.ts.map +1 -0
- package/dist/client-config-api-service/{client-config-api-service.mjs → client-config-api-service.js} +3 -3
- package/dist/client-config-api-service/client-config-api-service.js.map +1 -0
- package/dist/{constants.d.cts → constants.d.ts} +1 -1
- package/dist/constants.d.ts.map +1 -0
- package/dist/{constants.mjs → constants.js} +1 -1
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/{remote-feature-flag-controller-method-action-types.d.cts → remote-feature-flag-controller-method-action-types.d.ts} +2 -2
- package/dist/remote-feature-flag-controller-method-action-types.d.ts.map +1 -0
- package/dist/{remote-feature-flag-controller-method-action-types.mjs → remote-feature-flag-controller-method-action-types.js} +1 -1
- package/dist/remote-feature-flag-controller-method-action-types.js.map +1 -0
- package/dist/{remote-feature-flag-controller-types.d.mts → remote-feature-flag-controller-types.d.ts} +2 -2
- package/dist/remote-feature-flag-controller-types.d.ts.map +1 -0
- package/dist/{remote-feature-flag-controller-types.mjs → remote-feature-flag-controller-types.js} +1 -1
- package/dist/remote-feature-flag-controller-types.js.map +1 -0
- package/dist/{remote-feature-flag-controller.d.cts → remote-feature-flag-controller.d.ts} +8 -8
- package/dist/remote-feature-flag-controller.d.ts.map +1 -0
- package/dist/{remote-feature-flag-controller.mjs → remote-feature-flag-controller.js} +5 -5
- package/dist/remote-feature-flag-controller.js.map +1 -0
- package/dist/utils/{user-segmentation-utils.d.cts → user-segmentation-utils.d.ts} +3 -3
- package/dist/utils/user-segmentation-utils.d.ts.map +1 -0
- package/dist/utils/{user-segmentation-utils.mjs → user-segmentation-utils.js} +3 -3
- package/dist/utils/user-segmentation-utils.js.map +1 -0
- package/dist/utils/{version.d.cts → version.d.ts} +4 -4
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/{version.mjs → version.js} +2 -2
- package/dist/utils/version.js.map +1 -0
- package/package.json +12 -16
- package/dist/client-config-api-service/abstract-client-config-api-service.cjs +0 -3
- package/dist/client-config-api-service/abstract-client-config-api-service.cjs.map +0 -1
- package/dist/client-config-api-service/abstract-client-config-api-service.d.cts +0 -17
- package/dist/client-config-api-service/abstract-client-config-api-service.d.cts.map +0 -1
- package/dist/client-config-api-service/abstract-client-config-api-service.d.mts.map +0 -1
- package/dist/client-config-api-service/abstract-client-config-api-service.mjs +0 -2
- package/dist/client-config-api-service/abstract-client-config-api-service.mjs.map +0 -1
- package/dist/client-config-api-service/client-config-api-service.cjs +0 -94
- package/dist/client-config-api-service/client-config-api-service.cjs.map +0 -1
- package/dist/client-config-api-service/client-config-api-service.d.cts.map +0 -1
- package/dist/client-config-api-service/client-config-api-service.d.mts +0 -108
- package/dist/client-config-api-service/client-config-api-service.d.mts.map +0 -1
- package/dist/client-config-api-service/client-config-api-service.mjs.map +0 -1
- package/dist/constants.cjs +0 -8
- package/dist/constants.cjs.map +0 -1
- package/dist/constants.d.cts.map +0 -1
- package/dist/constants.d.mts +0 -5
- package/dist/constants.d.mts.map +0 -1
- package/dist/constants.mjs.map +0 -1
- package/dist/index.cjs +0 -15
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -8
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.mts +0 -8
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -5
- package/dist/index.mjs.map +0 -1
- package/dist/remote-feature-flag-controller-method-action-types.cjs +0 -7
- package/dist/remote-feature-flag-controller-method-action-types.cjs.map +0 -1
- package/dist/remote-feature-flag-controller-method-action-types.d.cts.map +0 -1
- package/dist/remote-feature-flag-controller-method-action-types.d.mts +0 -62
- package/dist/remote-feature-flag-controller-method-action-types.d.mts.map +0 -1
- package/dist/remote-feature-flag-controller-method-action-types.mjs.map +0 -1
- package/dist/remote-feature-flag-controller-types.cjs +0 -32
- package/dist/remote-feature-flag-controller-types.cjs.map +0 -1
- package/dist/remote-feature-flag-controller-types.d.cts +0 -64
- package/dist/remote-feature-flag-controller-types.d.cts.map +0 -1
- package/dist/remote-feature-flag-controller-types.d.mts.map +0 -1
- package/dist/remote-feature-flag-controller-types.mjs.map +0 -1
- package/dist/remote-feature-flag-controller.cjs +0 -436
- package/dist/remote-feature-flag-controller.cjs.map +0 -1
- package/dist/remote-feature-flag-controller.d.cts.map +0 -1
- package/dist/remote-feature-flag-controller.d.mts +0 -113
- package/dist/remote-feature-flag-controller.d.mts.map +0 -1
- package/dist/remote-feature-flag-controller.mjs.map +0 -1
- package/dist/utils/user-segmentation-utils.cjs +0 -107
- package/dist/utils/user-segmentation-utils.cjs.map +0 -1
- package/dist/utils/user-segmentation-utils.d.cts.map +0 -1
- package/dist/utils/user-segmentation-utils.d.mts +0 -34
- package/dist/utils/user-segmentation-utils.d.mts.map +0 -1
- package/dist/utils/user-segmentation-utils.mjs.map +0 -1
- package/dist/utils/version.cjs +0 -78
- package/dist/utils/version.cjs.map +0 -1
- package/dist/utils/version.d.cts.map +0 -1
- package/dist/utils/version.d.mts +0 -26
- package/dist/utils/version.d.mts.map +0 -1
- package/dist/utils/version.mjs.map +0 -1
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
-
};
|
|
8
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
-
};
|
|
13
|
-
var _RemoteFeatureFlagController_instances, _RemoteFeatureFlagController_fetchInterval, _RemoteFeatureFlagController_disabled, _RemoteFeatureFlagController_clientConfigApiService, _RemoteFeatureFlagController_inProgressFlagUpdate, _RemoteFeatureFlagController_getMetaMetricsId, _RemoteFeatureFlagController_getCanonicalProfileId, _RemoteFeatureFlagController_metaMetricsFlags, _RemoteFeatureFlagController_clientVersion, _RemoteFeatureFlagController_defaultFeatureFlags, _RemoteFeatureFlagController_processedRemoteFeatureFlags, _RemoteFeatureFlagController_getEffectiveFeatureFlags, _RemoteFeatureFlagController_isCacheExpired, _RemoteFeatureFlagController_updateCache, _RemoteFeatureFlagController_processVersionBasedFlag, _RemoteFeatureFlagController_getSegmentationId, _RemoteFeatureFlagController_processRemoteFeatureFlags;
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.RemoteFeatureFlagController = exports.getDefaultRemoteFeatureFlagControllerState = exports.DEFAULT_CACHE_DURATION = exports.controllerName = void 0;
|
|
16
|
-
const base_controller_1 = require("@metamask/base-controller");
|
|
17
|
-
const utils_1 = require("@metamask/utils");
|
|
18
|
-
const user_segmentation_utils_js_1 = require("./utils/user-segmentation-utils.cjs");
|
|
19
|
-
const version_js_1 = require("./utils/version.cjs");
|
|
20
|
-
// === GENERAL ===
|
|
21
|
-
exports.controllerName = 'RemoteFeatureFlagController';
|
|
22
|
-
exports.DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day
|
|
23
|
-
const remoteFeatureFlagControllerMetadata = {
|
|
24
|
-
remoteFeatureFlags: {
|
|
25
|
-
includeInStateLogs: true,
|
|
26
|
-
persist: true,
|
|
27
|
-
includeInDebugSnapshot: true,
|
|
28
|
-
usedInUi: true,
|
|
29
|
-
},
|
|
30
|
-
localOverrides: {
|
|
31
|
-
includeInStateLogs: true,
|
|
32
|
-
persist: true,
|
|
33
|
-
includeInDebugSnapshot: true,
|
|
34
|
-
usedInUi: true,
|
|
35
|
-
},
|
|
36
|
-
rawRemoteFeatureFlags: {
|
|
37
|
-
includeInStateLogs: true,
|
|
38
|
-
persist: true,
|
|
39
|
-
includeInDebugSnapshot: true,
|
|
40
|
-
usedInUi: false,
|
|
41
|
-
},
|
|
42
|
-
cacheTimestamp: {
|
|
43
|
-
includeInStateLogs: true,
|
|
44
|
-
persist: true,
|
|
45
|
-
includeInDebugSnapshot: true,
|
|
46
|
-
usedInUi: false,
|
|
47
|
-
},
|
|
48
|
-
thresholdCache: {
|
|
49
|
-
includeInStateLogs: false,
|
|
50
|
-
persist: true,
|
|
51
|
-
includeInDebugSnapshot: false,
|
|
52
|
-
usedInUi: false,
|
|
53
|
-
},
|
|
54
|
-
featureFlagThresholdGroups: {
|
|
55
|
-
includeInStateLogs: true,
|
|
56
|
-
persist: true,
|
|
57
|
-
includeInDebugSnapshot: true,
|
|
58
|
-
usedInUi: false,
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
// === MESSENGER ===
|
|
62
|
-
const MESSENGER_EXPOSED_METHODS = [
|
|
63
|
-
'clearAllFlagOverrides',
|
|
64
|
-
'disable',
|
|
65
|
-
'enable',
|
|
66
|
-
'removeFlagOverride',
|
|
67
|
-
'setFlagOverride',
|
|
68
|
-
'updateRemoteFeatureFlags',
|
|
69
|
-
];
|
|
70
|
-
/**
|
|
71
|
-
* Returns the default state for the RemoteFeatureFlagController.
|
|
72
|
-
*
|
|
73
|
-
* @returns The default controller state.
|
|
74
|
-
*/
|
|
75
|
-
function getDefaultRemoteFeatureFlagControllerState() {
|
|
76
|
-
return {
|
|
77
|
-
remoteFeatureFlags: {},
|
|
78
|
-
localOverrides: {},
|
|
79
|
-
rawRemoteFeatureFlags: {},
|
|
80
|
-
cacheTimestamp: 0,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
exports.getDefaultRemoteFeatureFlagControllerState = getDefaultRemoteFeatureFlagControllerState;
|
|
84
|
-
/**
|
|
85
|
-
* Searches threshold entries for an explicit MetaMetrics ID match.
|
|
86
|
-
* Returns the first entry whose `metaMetricsIds` list contains the given
|
|
87
|
-
* normalized ID. Entries with malformed `metaMetricsIds` (not an array) are
|
|
88
|
-
* skipped without throwing.
|
|
89
|
-
*
|
|
90
|
-
* @param entries - The array of raw threshold entries for a feature flag.
|
|
91
|
-
* @param normalizedId - The current user's MetaMetrics ID, already trimmed and
|
|
92
|
-
* lower-cased.
|
|
93
|
-
* @returns The first matching entry, or `undefined` if none match.
|
|
94
|
-
*/
|
|
95
|
-
function findExplicitIdMatch(entries, normalizedId) {
|
|
96
|
-
for (const entry of entries) {
|
|
97
|
-
if (!(0, user_segmentation_utils_js_1.isFeatureFlagWithScopeValue)(entry)) {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
const { metaMetricsIds } = entry;
|
|
101
|
-
if (!Array.isArray(metaMetricsIds)) {
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
const hasMatch = metaMetricsIds.some((id) => typeof id === 'string' && id.trim().toLowerCase() === normalizedId);
|
|
105
|
-
if (hasMatch) {
|
|
106
|
-
return entry;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return undefined;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.
|
|
113
|
-
* It fetches feature flags from a remote API, caches them, and provides methods to access
|
|
114
|
-
* and manage these flags. The controller ensures that feature flags are refreshed based on
|
|
115
|
-
* a specified interval and handles cases where the controller is disabled or the network is unavailable.
|
|
116
|
-
*/
|
|
117
|
-
class RemoteFeatureFlagController extends base_controller_1.BaseController {
|
|
118
|
-
/**
|
|
119
|
-
* Constructs a new RemoteFeatureFlagController instance.
|
|
120
|
-
*
|
|
121
|
-
* @param options - The controller options.
|
|
122
|
-
* @param options.messenger - The messenger used for communication.
|
|
123
|
-
* @param options.state - The initial state of the controller.
|
|
124
|
-
* @param options.clientConfigApiService - The service instance to fetch remote feature flags.
|
|
125
|
-
* @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.
|
|
126
|
-
* @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.
|
|
127
|
-
* @param options.getMetaMetricsId - Returns metaMetricsId.
|
|
128
|
-
* @param options.getCanonicalProfileId - Returns the canonical profile identifier used for threshold flags by default. Must return an empty string, rather than throwing, when the identifier is unavailable.
|
|
129
|
-
* @param options.metaMetricsFlags - Names of feature flags that should use MetaMetrics ID for threshold assignment.
|
|
130
|
-
* @param options.clientVersion - The current client version for version-based feature flag filtering. Must be a valid 3-part SemVer version string.
|
|
131
|
-
* @param options.prevClientVersion - The previous client version for feature flag cache invalidation.
|
|
132
|
-
* @param options.defaultFeatureFlags - Client-side default feature flags used as the lowest-precedence layer under processed remote flags and local overrides. Not persisted.
|
|
133
|
-
*/
|
|
134
|
-
constructor({ messenger, state, clientConfigApiService, fetchInterval = exports.DEFAULT_CACHE_DURATION, disabled = false, getMetaMetricsId, getCanonicalProfileId, metaMetricsFlags = [], clientVersion, prevClientVersion, defaultFeatureFlags = {}, }) {
|
|
135
|
-
if (!(0, utils_1.isValidSemVerVersion)(clientVersion)) {
|
|
136
|
-
throw new Error(`Invalid clientVersion: "${clientVersion}". Must be a valid 3-part SemVer version string`);
|
|
137
|
-
}
|
|
138
|
-
const initialState = {
|
|
139
|
-
...getDefaultRemoteFeatureFlagControllerState(),
|
|
140
|
-
...state,
|
|
141
|
-
};
|
|
142
|
-
const hasClientVersionChanged = (0, utils_1.isValidSemVerVersion)(prevClientVersion) &&
|
|
143
|
-
prevClientVersion !== clientVersion;
|
|
144
|
-
super({
|
|
145
|
-
name: exports.controllerName,
|
|
146
|
-
metadata: remoteFeatureFlagControllerMetadata,
|
|
147
|
-
messenger,
|
|
148
|
-
state: {
|
|
149
|
-
...initialState,
|
|
150
|
-
cacheTimestamp: hasClientVersionChanged
|
|
151
|
-
? 0
|
|
152
|
-
: initialState.cacheTimestamp,
|
|
153
|
-
},
|
|
154
|
-
});
|
|
155
|
-
_RemoteFeatureFlagController_instances.add(this);
|
|
156
|
-
_RemoteFeatureFlagController_fetchInterval.set(this, void 0);
|
|
157
|
-
_RemoteFeatureFlagController_disabled.set(this, void 0);
|
|
158
|
-
_RemoteFeatureFlagController_clientConfigApiService.set(this, void 0);
|
|
159
|
-
_RemoteFeatureFlagController_inProgressFlagUpdate.set(this, void 0);
|
|
160
|
-
_RemoteFeatureFlagController_getMetaMetricsId.set(this, void 0);
|
|
161
|
-
_RemoteFeatureFlagController_getCanonicalProfileId.set(this, void 0);
|
|
162
|
-
_RemoteFeatureFlagController_metaMetricsFlags.set(this, void 0);
|
|
163
|
-
_RemoteFeatureFlagController_clientVersion.set(this, void 0);
|
|
164
|
-
_RemoteFeatureFlagController_defaultFeatureFlags.set(this, void 0);
|
|
165
|
-
_RemoteFeatureFlagController_processedRemoteFeatureFlags.set(this, void 0);
|
|
166
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_defaultFeatureFlags, defaultFeatureFlags, "f");
|
|
167
|
-
// Last session's effective flags stand in for the remote layer until
|
|
168
|
-
// `init` re-derives it from the persisted raw flags, or a fetch replaces
|
|
169
|
-
// it. Overrides are layered on top rather than subtracted out, so a remote
|
|
170
|
-
// flag that happens to share an override's value is not lost.
|
|
171
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_processedRemoteFeatureFlags, initialState.remoteFeatureFlags, "f");
|
|
172
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_fetchInterval, fetchInterval, "f");
|
|
173
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_disabled, disabled, "f");
|
|
174
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_clientConfigApiService, clientConfigApiService, "f");
|
|
175
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_getMetaMetricsId, getMetaMetricsId, "f");
|
|
176
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_getCanonicalProfileId, getCanonicalProfileId, "f");
|
|
177
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_metaMetricsFlags, new Set(metaMetricsFlags), "f");
|
|
178
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_clientVersion, clientVersion, "f");
|
|
179
|
-
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Retrieves the remote feature flags, fetching from the API if necessary.
|
|
183
|
-
* Uses caching to prevent redundant API calls and handles concurrent fetches.
|
|
184
|
-
*
|
|
185
|
-
* @param force - When `true`, fetch even if the cache has not expired.
|
|
186
|
-
* Defaults to `false`. Has no effect when the controller is disabled.
|
|
187
|
-
* @returns A promise that resolves to the current set of feature flags.
|
|
188
|
-
*/
|
|
189
|
-
async updateRemoteFeatureFlags(force = false) {
|
|
190
|
-
if (__classPrivateFieldGet(this, _RemoteFeatureFlagController_disabled, "f") || (!force && !__classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_isCacheExpired).call(this))) {
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
let serverData;
|
|
194
|
-
if (__classPrivateFieldGet(this, _RemoteFeatureFlagController_inProgressFlagUpdate, "f")) {
|
|
195
|
-
await __classPrivateFieldGet(this, _RemoteFeatureFlagController_inProgressFlagUpdate, "f");
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
try {
|
|
199
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_inProgressFlagUpdate, __classPrivateFieldGet(this, _RemoteFeatureFlagController_clientConfigApiService, "f").fetchRemoteFeatureFlags(), "f");
|
|
200
|
-
serverData = await __classPrivateFieldGet(this, _RemoteFeatureFlagController_inProgressFlagUpdate, "f");
|
|
201
|
-
}
|
|
202
|
-
finally {
|
|
203
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_inProgressFlagUpdate, undefined, "f");
|
|
204
|
-
}
|
|
205
|
-
await __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_updateCache).call(this, serverData.remoteFeatureFlags);
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Computes the effective feature flags, re-deriving the remote layer from the
|
|
209
|
-
* raw flags already in state. Threshold selection needs to await a hash and
|
|
210
|
-
* so cannot run in the constructor, which is why this cannot be part of
|
|
211
|
-
* construction. Clients must call this once after constructing the
|
|
212
|
-
* controller.
|
|
213
|
-
*
|
|
214
|
-
* When there are no persisted raw flags, as on a fresh install or for state
|
|
215
|
-
* persisted before raw flags were stored, the previous session's flags stand
|
|
216
|
-
* in for the remote layer so that nothing is lost.
|
|
217
|
-
*/
|
|
218
|
-
async init() {
|
|
219
|
-
const { rawRemoteFeatureFlags } = this.state;
|
|
220
|
-
const hasRawRemoteFeatureFlags = rawRemoteFeatureFlags && Object.keys(rawRemoteFeatureFlags).length > 0;
|
|
221
|
-
const resolved = hasRawRemoteFeatureFlags
|
|
222
|
-
? await __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_processRemoteFeatureFlags).call(this, rawRemoteFeatureFlags)
|
|
223
|
-
: undefined;
|
|
224
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_processedRemoteFeatureFlags, resolved?.processedFlags ?? this.state.remoteFeatureFlags, "f");
|
|
225
|
-
this.update(() => {
|
|
226
|
-
return {
|
|
227
|
-
...this.state,
|
|
228
|
-
remoteFeatureFlags: __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_getEffectiveFeatureFlags).call(this),
|
|
229
|
-
...(resolved && {
|
|
230
|
-
thresholdCache: resolved.thresholdCache,
|
|
231
|
-
featureFlagThresholdGroups: resolved.featureFlagThresholdGroups,
|
|
232
|
-
}),
|
|
233
|
-
};
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Enables the controller, allowing it to make network requests.
|
|
238
|
-
*/
|
|
239
|
-
enable() {
|
|
240
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_disabled, false, "f");
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Disables the controller, preventing it from making network requests.
|
|
244
|
-
*/
|
|
245
|
-
disable() {
|
|
246
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_disabled, true, "f");
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Sets a local override for a specific feature flag.
|
|
250
|
-
*
|
|
251
|
-
* @param flagName - The name of the feature flag to override.
|
|
252
|
-
* @param value - The override value for the feature flag.
|
|
253
|
-
*/
|
|
254
|
-
setFlagOverride(flagName, value) {
|
|
255
|
-
this.update(() => {
|
|
256
|
-
const localOverrides = {
|
|
257
|
-
...this.state.localOverrides,
|
|
258
|
-
[flagName]: value,
|
|
259
|
-
};
|
|
260
|
-
return {
|
|
261
|
-
...this.state,
|
|
262
|
-
localOverrides,
|
|
263
|
-
remoteFeatureFlags: __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_getEffectiveFeatureFlags).call(this, { localOverrides }),
|
|
264
|
-
};
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Clears the local override for a specific feature flag.
|
|
269
|
-
*
|
|
270
|
-
* @param flagName - The name of the feature flag to clear.
|
|
271
|
-
*/
|
|
272
|
-
removeFlagOverride(flagName) {
|
|
273
|
-
const newLocalOverrides = { ...this.state.localOverrides };
|
|
274
|
-
delete newLocalOverrides[flagName];
|
|
275
|
-
this.update(() => {
|
|
276
|
-
return {
|
|
277
|
-
...this.state,
|
|
278
|
-
localOverrides: newLocalOverrides,
|
|
279
|
-
remoteFeatureFlags: __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_getEffectiveFeatureFlags).call(this, {
|
|
280
|
-
localOverrides: newLocalOverrides,
|
|
281
|
-
}),
|
|
282
|
-
};
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Clears all local feature flag overrides.
|
|
287
|
-
*/
|
|
288
|
-
clearAllFlagOverrides() {
|
|
289
|
-
this.update(() => {
|
|
290
|
-
return {
|
|
291
|
-
...this.state,
|
|
292
|
-
localOverrides: {},
|
|
293
|
-
remoteFeatureFlags: __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_getEffectiveFeatureFlags).call(this, {
|
|
294
|
-
localOverrides: {},
|
|
295
|
-
}),
|
|
296
|
-
};
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
exports.RemoteFeatureFlagController = RemoteFeatureFlagController;
|
|
301
|
-
_RemoteFeatureFlagController_fetchInterval = new WeakMap(), _RemoteFeatureFlagController_disabled = new WeakMap(), _RemoteFeatureFlagController_clientConfigApiService = new WeakMap(), _RemoteFeatureFlagController_inProgressFlagUpdate = new WeakMap(), _RemoteFeatureFlagController_getMetaMetricsId = new WeakMap(), _RemoteFeatureFlagController_getCanonicalProfileId = new WeakMap(), _RemoteFeatureFlagController_metaMetricsFlags = new WeakMap(), _RemoteFeatureFlagController_clientVersion = new WeakMap(), _RemoteFeatureFlagController_defaultFeatureFlags = new WeakMap(), _RemoteFeatureFlagController_processedRemoteFeatureFlags = new WeakMap(), _RemoteFeatureFlagController_instances = new WeakSet(), _RemoteFeatureFlagController_getEffectiveFeatureFlags = function _RemoteFeatureFlagController_getEffectiveFeatureFlags({ processedRemoteFeatureFlags = __classPrivateFieldGet(this, _RemoteFeatureFlagController_processedRemoteFeatureFlags, "f"), localOverrides = this.state.localOverrides, } = {}) {
|
|
302
|
-
return {
|
|
303
|
-
...__classPrivateFieldGet(this, _RemoteFeatureFlagController_defaultFeatureFlags, "f"),
|
|
304
|
-
...processedRemoteFeatureFlags,
|
|
305
|
-
...localOverrides,
|
|
306
|
-
};
|
|
307
|
-
}, _RemoteFeatureFlagController_isCacheExpired = function _RemoteFeatureFlagController_isCacheExpired() {
|
|
308
|
-
return Date.now() - this.state.cacheTimestamp > __classPrivateFieldGet(this, _RemoteFeatureFlagController_fetchInterval, "f");
|
|
309
|
-
}, _RemoteFeatureFlagController_updateCache =
|
|
310
|
-
/**
|
|
311
|
-
* Updates the controller's state with new feature flags and resets the cache timestamp.
|
|
312
|
-
*
|
|
313
|
-
* @param remoteFeatureFlags - The new feature flags to cache.
|
|
314
|
-
*/
|
|
315
|
-
async function _RemoteFeatureFlagController_updateCache(remoteFeatureFlags) {
|
|
316
|
-
const resolved = await __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_processRemoteFeatureFlags).call(this, remoteFeatureFlags);
|
|
317
|
-
__classPrivateFieldSet(this, _RemoteFeatureFlagController_processedRemoteFeatureFlags, resolved.processedFlags, "f");
|
|
318
|
-
// Single state update with all changes batched together
|
|
319
|
-
this.update(() => {
|
|
320
|
-
return {
|
|
321
|
-
...this.state,
|
|
322
|
-
remoteFeatureFlags: __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_getEffectiveFeatureFlags).call(this, {
|
|
323
|
-
processedRemoteFeatureFlags: resolved.processedFlags,
|
|
324
|
-
}),
|
|
325
|
-
rawRemoteFeatureFlags: remoteFeatureFlags,
|
|
326
|
-
cacheTimestamp: Date.now(),
|
|
327
|
-
thresholdCache: resolved.thresholdCache,
|
|
328
|
-
featureFlagThresholdGroups: resolved.featureFlagThresholdGroups,
|
|
329
|
-
};
|
|
330
|
-
});
|
|
331
|
-
}, _RemoteFeatureFlagController_processVersionBasedFlag = function _RemoteFeatureFlagController_processVersionBasedFlag(flagValue) {
|
|
332
|
-
if (!(0, version_js_1.isVersionFeatureFlag)(flagValue)) {
|
|
333
|
-
return flagValue;
|
|
334
|
-
}
|
|
335
|
-
return (0, version_js_1.getVersionData)(flagValue, __classPrivateFieldGet(this, _RemoteFeatureFlagController_clientVersion, "f"));
|
|
336
|
-
}, _RemoteFeatureFlagController_getSegmentationId = function _RemoteFeatureFlagController_getSegmentationId(featureFlagName) {
|
|
337
|
-
if (__classPrivateFieldGet(this, _RemoteFeatureFlagController_metaMetricsFlags, "f").has(featureFlagName)) {
|
|
338
|
-
return __classPrivateFieldGet(this, _RemoteFeatureFlagController_getMetaMetricsId, "f").call(this);
|
|
339
|
-
}
|
|
340
|
-
return __classPrivateFieldGet(this, _RemoteFeatureFlagController_getCanonicalProfileId, "f").call(this);
|
|
341
|
-
}, _RemoteFeatureFlagController_processRemoteFeatureFlags =
|
|
342
|
-
/**
|
|
343
|
-
* Resolves raw feature flags into the values that apply to this client and
|
|
344
|
-
* user, selecting version and threshold entries and reconciling the
|
|
345
|
-
* threshold cache against the flags the server currently serves.
|
|
346
|
-
*
|
|
347
|
-
* @param remoteFeatureFlags - The unprocessed feature flags.
|
|
348
|
-
* @returns The processed flags, the updated threshold cache, and the
|
|
349
|
-
* selected threshold group names.
|
|
350
|
-
*/
|
|
351
|
-
async function _RemoteFeatureFlagController_processRemoteFeatureFlags(remoteFeatureFlags) {
|
|
352
|
-
const processedFlags = {};
|
|
353
|
-
const metaMetricsId = __classPrivateFieldGet(this, _RemoteFeatureFlagController_getMetaMetricsId, "f").call(this);
|
|
354
|
-
const thresholdCacheUpdates = {};
|
|
355
|
-
const featureFlagThresholdGroups = {};
|
|
356
|
-
for (const [remoteFeatureFlagName, remoteFeatureFlagValue,] of Object.entries(remoteFeatureFlags)) {
|
|
357
|
-
let processedValue = __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_processVersionBasedFlag).call(this, remoteFeatureFlagValue);
|
|
358
|
-
if (processedValue === null) {
|
|
359
|
-
continue;
|
|
360
|
-
}
|
|
361
|
-
if (Array.isArray(processedValue)) {
|
|
362
|
-
// Validate array has valid threshold items before doing expensive crypto operation
|
|
363
|
-
const hasValidThresholds = processedValue.some(user_segmentation_utils_js_1.isFeatureFlagWithScopeValue);
|
|
364
|
-
if (!hasValidThresholds) {
|
|
365
|
-
// Not a threshold array - preserve as-is
|
|
366
|
-
processedFlags[remoteFeatureFlagName] = processedValue;
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
// Explicit-ID matching: check before hash-based threshold, bypasses cache
|
|
370
|
-
const normalizedMetaMetricsId = metaMetricsId.trim().toLowerCase();
|
|
371
|
-
const explicitMatch = normalizedMetaMetricsId
|
|
372
|
-
? findExplicitIdMatch(processedValue, normalizedMetaMetricsId)
|
|
373
|
-
: undefined;
|
|
374
|
-
if (explicitMatch) {
|
|
375
|
-
processedValue = explicitMatch.value;
|
|
376
|
-
if (explicitMatch.name) {
|
|
377
|
-
featureFlagThresholdGroups[remoteFeatureFlagName] =
|
|
378
|
-
explicitMatch.name;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
const segmentationId = __classPrivateFieldGet(this, _RemoteFeatureFlagController_instances, "m", _RemoteFeatureFlagController_getSegmentationId).call(this, remoteFeatureFlagName);
|
|
383
|
-
if (!segmentationId) {
|
|
384
|
-
processedFlags[remoteFeatureFlagName] = processedValue;
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
// Fall back to hash-based threshold selection with cache
|
|
388
|
-
const cacheKey = `${segmentationId}:${remoteFeatureFlagName}`;
|
|
389
|
-
let thresholdValue = this.state.thresholdCache?.[cacheKey];
|
|
390
|
-
if (thresholdValue === undefined) {
|
|
391
|
-
thresholdValue = await (0, user_segmentation_utils_js_1.calculateThresholdForFlag)(segmentationId, remoteFeatureFlagName);
|
|
392
|
-
// Collect new threshold for batched state update
|
|
393
|
-
thresholdCacheUpdates[cacheKey] = thresholdValue;
|
|
394
|
-
}
|
|
395
|
-
const threshold = thresholdValue;
|
|
396
|
-
const selectedGroup = processedValue.find((featureFlag) => {
|
|
397
|
-
if (!(0, user_segmentation_utils_js_1.isFeatureFlagWithScopeValue)(featureFlag)) {
|
|
398
|
-
return false;
|
|
399
|
-
}
|
|
400
|
-
return threshold <= featureFlag.scope.value;
|
|
401
|
-
});
|
|
402
|
-
if (selectedGroup) {
|
|
403
|
-
processedValue = selectedGroup.value;
|
|
404
|
-
if (selectedGroup.name) {
|
|
405
|
-
featureFlagThresholdGroups[remoteFeatureFlagName] =
|
|
406
|
-
selectedGroup.name;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
processedFlags[remoteFeatureFlagName] = processedValue;
|
|
412
|
-
}
|
|
413
|
-
const thresholdCache = {
|
|
414
|
-
...this.state.thresholdCache,
|
|
415
|
-
...thresholdCacheUpdates,
|
|
416
|
-
};
|
|
417
|
-
// Drop cached thresholds for flags this user is no longer served, under
|
|
418
|
-
// either identifier they may have been bucketed by.
|
|
419
|
-
const canonicalProfileId = __classPrivateFieldGet(this, _RemoteFeatureFlagController_getCanonicalProfileId, "f").call(this);
|
|
420
|
-
const currentFlagNames = Object.keys(remoteFeatureFlags);
|
|
421
|
-
for (const cacheKey of Object.keys(thresholdCache)) {
|
|
422
|
-
const [cachedSegmentationId, ...cachedFlagNameParts] = cacheKey.split(':');
|
|
423
|
-
const cachedFlagName = cachedFlagNameParts.join(':');
|
|
424
|
-
if ((cachedSegmentationId === metaMetricsId ||
|
|
425
|
-
cachedSegmentationId === canonicalProfileId) &&
|
|
426
|
-
!currentFlagNames.includes(cachedFlagName)) {
|
|
427
|
-
delete thresholdCache[cacheKey];
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return {
|
|
431
|
-
processedFlags,
|
|
432
|
-
thresholdCache,
|
|
433
|
-
featureFlagThresholdGroups,
|
|
434
|
-
};
|
|
435
|
-
};
|
|
436
|
-
//# sourceMappingURL=remote-feature-flag-controller.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller.cjs","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseController,\n ControllerGetStateAction,\n} from '@metamask/base-controller';\nimport type { ControllerStateChangeEvent } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport { isValidSemVerVersion } from '@metamask/utils';\nimport type { Json, SemVerVersion } from '@metamask/utils';\n\nimport type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service.js';\nimport type { RemoteFeatureFlagControllerMethodActions } from './remote-feature-flag-controller-method-action-types.js';\nimport type {\n FeatureFlags,\n ServiceResponse,\n FeatureFlagScopeValue,\n} from './remote-feature-flag-controller-types.js';\nimport {\n calculateThresholdForFlag,\n isFeatureFlagWithScopeValue,\n} from './utils/user-segmentation-utils.js';\nimport { isVersionFeatureFlag, getVersionData } from './utils/version.js';\n\n// === GENERAL ===\n\nexport const controllerName = 'RemoteFeatureFlagController';\nexport const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day\n\n// === STATE ===\n\nexport type RemoteFeatureFlagControllerState = {\n remoteFeatureFlags: FeatureFlags;\n localOverrides?: FeatureFlags;\n rawRemoteFeatureFlags?: FeatureFlags;\n cacheTimestamp: number;\n thresholdCache?: Record<string, number>;\n featureFlagThresholdGroups?: Record<string, string>;\n};\n\nconst remoteFeatureFlagControllerMetadata = {\n remoteFeatureFlags: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n localOverrides: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n rawRemoteFeatureFlags: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n cacheTimestamp: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n thresholdCache: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: false,\n },\n featureFlagThresholdGroups: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n};\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'clearAllFlagOverrides',\n 'disable',\n 'enable',\n 'removeFlagOverride',\n 'setFlagOverride',\n 'updateRemoteFeatureFlags',\n] as const;\n\nexport type RemoteFeatureFlagControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerActions =\n | RemoteFeatureFlagControllerGetStateAction\n | RemoteFeatureFlagControllerMethodActions;\n\nexport type RemoteFeatureFlagControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n RemoteFeatureFlagControllerState\n >;\n\nexport type RemoteFeatureFlagControllerEvents =\n RemoteFeatureFlagControllerStateChangeEvent;\n\nexport type RemoteFeatureFlagControllerMessenger = Messenger<\n typeof controllerName,\n RemoteFeatureFlagControllerActions,\n RemoteFeatureFlagControllerEvents\n>;\n\n/**\n * Returns the default state for the RemoteFeatureFlagController.\n *\n * @returns The default controller state.\n */\nexport function getDefaultRemoteFeatureFlagControllerState(): RemoteFeatureFlagControllerState {\n return {\n remoteFeatureFlags: {},\n localOverrides: {},\n rawRemoteFeatureFlags: {},\n cacheTimestamp: 0,\n };\n}\n\n/**\n * Searches threshold entries for an explicit MetaMetrics ID match.\n * Returns the first entry whose `metaMetricsIds` list contains the given\n * normalized ID. Entries with malformed `metaMetricsIds` (not an array) are\n * skipped without throwing.\n *\n * @param entries - The array of raw threshold entries for a feature flag.\n * @param normalizedId - The current user's MetaMetrics ID, already trimmed and\n * lower-cased.\n * @returns The first matching entry, or `undefined` if none match.\n */\nfunction findExplicitIdMatch(\n entries: Json[],\n normalizedId: string,\n): FeatureFlagScopeValue | undefined {\n for (const entry of entries) {\n if (!isFeatureFlagWithScopeValue(entry)) {\n continue;\n }\n const { metaMetricsIds } = entry;\n if (!Array.isArray(metaMetricsIds)) {\n continue;\n }\n const hasMatch = metaMetricsIds.some(\n (id) =>\n typeof id === 'string' && id.trim().toLowerCase() === normalizedId,\n );\n if (hasMatch) {\n return entry;\n }\n }\n return undefined;\n}\n\n/**\n * The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.\n * It fetches feature flags from a remote API, caches them, and provides methods to access\n * and manage these flags. The controller ensures that feature flags are refreshed based on\n * a specified interval and handles cases where the controller is disabled or the network is unavailable.\n */\nexport class RemoteFeatureFlagController extends BaseController<\n typeof controllerName,\n RemoteFeatureFlagControllerState,\n RemoteFeatureFlagControllerMessenger\n> {\n readonly #fetchInterval: number;\n\n #disabled: boolean;\n\n readonly #clientConfigApiService: AbstractClientConfigApiService;\n\n #inProgressFlagUpdate?: Promise<ServiceResponse>;\n\n readonly #getMetaMetricsId: () => string;\n\n readonly #getCanonicalProfileId: () => string;\n\n readonly #metaMetricsFlags: ReadonlySet<string>;\n\n readonly #clientVersion: SemVerVersion;\n\n readonly #defaultFeatureFlags: FeatureFlags;\n\n #processedRemoteFeatureFlags: FeatureFlags;\n\n /**\n * Constructs a new RemoteFeatureFlagController instance.\n *\n * @param options - The controller options.\n * @param options.messenger - The messenger used for communication.\n * @param options.state - The initial state of the controller.\n * @param options.clientConfigApiService - The service instance to fetch remote feature flags.\n * @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.\n * @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.\n * @param options.getMetaMetricsId - Returns metaMetricsId.\n * @param options.getCanonicalProfileId - Returns the canonical profile identifier used for threshold flags by default. Must return an empty string, rather than throwing, when the identifier is unavailable.\n * @param options.metaMetricsFlags - Names of feature flags that should use MetaMetrics ID for threshold assignment.\n * @param options.clientVersion - The current client version for version-based feature flag filtering. Must be a valid 3-part SemVer version string.\n * @param options.prevClientVersion - The previous client version for feature flag cache invalidation.\n * @param options.defaultFeatureFlags - Client-side default feature flags used as the lowest-precedence layer under processed remote flags and local overrides. Not persisted.\n */\n constructor({\n messenger,\n state,\n clientConfigApiService,\n fetchInterval = DEFAULT_CACHE_DURATION,\n disabled = false,\n getMetaMetricsId,\n getCanonicalProfileId,\n metaMetricsFlags = [],\n clientVersion,\n prevClientVersion,\n defaultFeatureFlags = {},\n }: {\n messenger: RemoteFeatureFlagControllerMessenger;\n state?: Partial<RemoteFeatureFlagControllerState>;\n clientConfigApiService: AbstractClientConfigApiService;\n getMetaMetricsId: () => string;\n getCanonicalProfileId: () => string;\n metaMetricsFlags?: readonly string[];\n fetchInterval?: number;\n disabled?: boolean;\n clientVersion: string;\n prevClientVersion?: string;\n defaultFeatureFlags?: FeatureFlags;\n }) {\n if (!isValidSemVerVersion(clientVersion)) {\n throw new Error(\n `Invalid clientVersion: \"${clientVersion}\". Must be a valid 3-part SemVer version string`,\n );\n }\n\n const initialState: RemoteFeatureFlagControllerState = {\n ...getDefaultRemoteFeatureFlagControllerState(),\n ...state,\n };\n\n const hasClientVersionChanged =\n isValidSemVerVersion(prevClientVersion) &&\n prevClientVersion !== clientVersion;\n\n super({\n name: controllerName,\n metadata: remoteFeatureFlagControllerMetadata,\n messenger,\n state: {\n ...initialState,\n cacheTimestamp: hasClientVersionChanged\n ? 0\n : initialState.cacheTimestamp,\n },\n });\n\n this.#defaultFeatureFlags = defaultFeatureFlags;\n // Last session's effective flags stand in for the remote layer until\n // `init` re-derives it from the persisted raw flags, or a fetch replaces\n // it. Overrides are layered on top rather than subtracted out, so a remote\n // flag that happens to share an override's value is not lost.\n this.#processedRemoteFeatureFlags = initialState.remoteFeatureFlags;\n this.#fetchInterval = fetchInterval;\n this.#disabled = disabled;\n this.#clientConfigApiService = clientConfigApiService;\n this.#getMetaMetricsId = getMetaMetricsId;\n this.#getCanonicalProfileId = getCanonicalProfileId;\n this.#metaMetricsFlags = new Set(metaMetricsFlags);\n this.#clientVersion = clientVersion;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Computes effective feature flags with precedence:\n * defaults < processed remote < local overrides.\n *\n * @param options - The layers to merge. Each defaults to the current layer.\n * @param options.processedRemoteFeatureFlags - The processed remote feature\n * flags. Defaults to the currently resolved remote layer.\n * @param options.localOverrides - Local overrides. Defaults to current state\n * overrides.\n * @returns The effective feature flags.\n */\n #getEffectiveFeatureFlags({\n processedRemoteFeatureFlags = this.#processedRemoteFeatureFlags,\n localOverrides = this.state.localOverrides,\n }: {\n processedRemoteFeatureFlags?: FeatureFlags;\n localOverrides?: FeatureFlags;\n } = {}): FeatureFlags {\n return {\n ...this.#defaultFeatureFlags,\n ...processedRemoteFeatureFlags,\n ...localOverrides,\n };\n }\n\n /**\n * Checks if the cached feature flags are expired based on the fetch interval.\n *\n * @returns Whether the cache is expired (`true`) or still valid (`false`).\n */\n #isCacheExpired(): boolean {\n return Date.now() - this.state.cacheTimestamp > this.#fetchInterval;\n }\n\n /**\n * Retrieves the remote feature flags, fetching from the API if necessary.\n * Uses caching to prevent redundant API calls and handles concurrent fetches.\n *\n * @param force - When `true`, fetch even if the cache has not expired.\n * Defaults to `false`. Has no effect when the controller is disabled.\n * @returns A promise that resolves to the current set of feature flags.\n */\n async updateRemoteFeatureFlags(force = false): Promise<void> {\n if (this.#disabled || (!force && !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 * Computes the effective feature flags, re-deriving the remote layer from the\n * raw flags already in state. Threshold selection needs to await a hash and\n * so cannot run in the constructor, which is why this cannot be part of\n * construction. Clients must call this once after constructing the\n * controller.\n *\n * When there are no persisted raw flags, as on a fresh install or for state\n * persisted before raw flags were stored, the previous session's flags stand\n * in for the remote layer so that nothing is lost.\n */\n async init(): Promise<void> {\n const { rawRemoteFeatureFlags } = this.state;\n const hasRawRemoteFeatureFlags =\n rawRemoteFeatureFlags && Object.keys(rawRemoteFeatureFlags).length > 0;\n\n const resolved = hasRawRemoteFeatureFlags\n ? await this.#processRemoteFeatureFlags(rawRemoteFeatureFlags)\n : undefined;\n\n this.#processedRemoteFeatureFlags =\n resolved?.processedFlags ?? this.state.remoteFeatureFlags;\n\n this.update(() => {\n return {\n ...this.state,\n remoteFeatureFlags: this.#getEffectiveFeatureFlags(),\n ...(resolved && {\n thresholdCache: resolved.thresholdCache,\n featureFlagThresholdGroups: resolved.featureFlagThresholdGroups,\n }),\n };\n });\n }\n\n /**\n * Updates the controller's state with new feature flags and resets the cache timestamp.\n *\n * @param remoteFeatureFlags - The new feature flags to cache.\n */\n async #updateCache(remoteFeatureFlags: FeatureFlags): Promise<void> {\n const resolved = await this.#processRemoteFeatureFlags(remoteFeatureFlags);\n\n this.#processedRemoteFeatureFlags = resolved.processedFlags;\n\n // Single state update with all changes batched together\n this.update(() => {\n return {\n ...this.state,\n remoteFeatureFlags: this.#getEffectiveFeatureFlags({\n processedRemoteFeatureFlags: resolved.processedFlags,\n }),\n rawRemoteFeatureFlags: remoteFeatureFlags,\n cacheTimestamp: Date.now(),\n thresholdCache: resolved.thresholdCache,\n featureFlagThresholdGroups: resolved.featureFlagThresholdGroups,\n };\n });\n }\n\n /**\n * Processes a version-based feature flag to get the appropriate value for the current client version.\n *\n * @param flagValue - The feature flag value to process\n * @returns The processed value, or null if no version qualifies (skip this flag)\n */\n #processVersionBasedFlag(flagValue: Json): Json | null {\n if (!isVersionFeatureFlag(flagValue)) {\n return flagValue;\n }\n\n return getVersionData(flagValue, this.#clientVersion);\n }\n\n /**\n * Selects the identifier used to bucket a threshold flag. Flags named in\n * `metaMetricsFlags` segment by MetaMetrics ID; all others segment by\n * canonical profile ID.\n *\n * @param featureFlagName - The name of the feature flag being processed.\n * @returns The segmentation identifier, which may be empty when unavailable.\n */\n #getSegmentationId(featureFlagName: string): string {\n if (this.#metaMetricsFlags.has(featureFlagName)) {\n return this.#getMetaMetricsId();\n }\n return this.#getCanonicalProfileId();\n }\n\n /**\n * Resolves raw feature flags into the values that apply to this client and\n * user, selecting version and threshold entries and reconciling the\n * threshold cache against the flags the server currently serves.\n *\n * @param remoteFeatureFlags - The unprocessed feature flags.\n * @returns The processed flags, the updated threshold cache, and the\n * selected threshold group names.\n */\n async #processRemoteFeatureFlags(remoteFeatureFlags: FeatureFlags): Promise<{\n processedFlags: FeatureFlags;\n thresholdCache: Record<string, number>;\n featureFlagThresholdGroups: Record<string, string>;\n }> {\n const processedFlags: FeatureFlags = {};\n const metaMetricsId = this.#getMetaMetricsId();\n const thresholdCacheUpdates: Record<string, number> = {};\n const featureFlagThresholdGroups: Record<string, string> = {};\n\n for (const [\n remoteFeatureFlagName,\n remoteFeatureFlagValue,\n ] of Object.entries(remoteFeatureFlags)) {\n let processedValue = this.#processVersionBasedFlag(\n remoteFeatureFlagValue,\n );\n if (processedValue === null) {\n continue;\n }\n\n if (Array.isArray(processedValue)) {\n // Validate array has valid threshold items before doing expensive crypto operation\n const hasValidThresholds = processedValue.some(\n isFeatureFlagWithScopeValue,\n );\n\n if (!hasValidThresholds) {\n // Not a threshold array - preserve as-is\n processedFlags[remoteFeatureFlagName] = processedValue;\n continue;\n }\n\n // Explicit-ID matching: check before hash-based threshold, bypasses cache\n const normalizedMetaMetricsId = metaMetricsId.trim().toLowerCase();\n const explicitMatch = normalizedMetaMetricsId\n ? findExplicitIdMatch(processedValue, normalizedMetaMetricsId)\n : undefined;\n\n if (explicitMatch) {\n processedValue = explicitMatch.value;\n if (explicitMatch.name) {\n featureFlagThresholdGroups[remoteFeatureFlagName] =\n explicitMatch.name;\n }\n } else {\n const segmentationId = this.#getSegmentationId(remoteFeatureFlagName);\n\n if (!segmentationId) {\n processedFlags[remoteFeatureFlagName] = processedValue;\n continue;\n }\n\n // Fall back to hash-based threshold selection with cache\n const cacheKey =\n `${segmentationId}:${remoteFeatureFlagName}` as const;\n let thresholdValue = this.state.thresholdCache?.[cacheKey];\n\n if (thresholdValue === undefined) {\n thresholdValue = await calculateThresholdForFlag(\n segmentationId,\n remoteFeatureFlagName,\n );\n\n // Collect new threshold for batched state update\n thresholdCacheUpdates[cacheKey] = thresholdValue;\n }\n\n const threshold = thresholdValue;\n const selectedGroup = processedValue.find(\n (featureFlag): featureFlag is FeatureFlagScopeValue => {\n if (!isFeatureFlagWithScopeValue(featureFlag)) {\n return false;\n }\n\n return threshold <= featureFlag.scope.value;\n },\n );\n\n if (selectedGroup) {\n processedValue = selectedGroup.value;\n if (selectedGroup.name) {\n featureFlagThresholdGroups[remoteFeatureFlagName] =\n selectedGroup.name;\n }\n }\n }\n }\n\n processedFlags[remoteFeatureFlagName] = processedValue;\n }\n\n const thresholdCache = {\n ...this.state.thresholdCache,\n ...thresholdCacheUpdates,\n };\n\n // Drop cached thresholds for flags this user is no longer served, under\n // either identifier they may have been bucketed by.\n const canonicalProfileId = this.#getCanonicalProfileId();\n const currentFlagNames = Object.keys(remoteFeatureFlags);\n for (const cacheKey of Object.keys(thresholdCache)) {\n const [cachedSegmentationId, ...cachedFlagNameParts] =\n cacheKey.split(':');\n const cachedFlagName = cachedFlagNameParts.join(':');\n if (\n (cachedSegmentationId === metaMetricsId ||\n cachedSegmentationId === canonicalProfileId) &&\n !currentFlagNames.includes(cachedFlagName)\n ) {\n delete thresholdCache[cacheKey];\n }\n }\n\n return {\n processedFlags,\n thresholdCache,\n featureFlagThresholdGroups,\n };\n }\n\n /**\n * Enables the controller, allowing it to make network requests.\n */\n enable(): void {\n this.#disabled = false;\n }\n\n /**\n * Disables the controller, preventing it from making network requests.\n */\n disable(): void {\n this.#disabled = true;\n }\n\n /**\n * Sets a local override for a specific feature flag.\n *\n * @param flagName - The name of the feature flag to override.\n * @param value - The override value for the feature flag.\n */\n setFlagOverride(flagName: string, value: Json): void {\n this.update(() => {\n const localOverrides = {\n ...this.state.localOverrides,\n [flagName]: value,\n };\n\n return {\n ...this.state,\n localOverrides,\n remoteFeatureFlags: this.#getEffectiveFeatureFlags({ localOverrides }),\n };\n });\n }\n\n /**\n * Clears the local override for a specific feature flag.\n *\n * @param flagName - The name of the feature flag to clear.\n */\n removeFlagOverride(flagName: string): void {\n const newLocalOverrides = { ...this.state.localOverrides };\n delete newLocalOverrides[flagName];\n\n this.update(() => {\n return {\n ...this.state,\n localOverrides: newLocalOverrides,\n remoteFeatureFlags: this.#getEffectiveFeatureFlags({\n localOverrides: newLocalOverrides,\n }),\n };\n });\n }\n\n /**\n * Clears all local feature flag overrides.\n */\n clearAllFlagOverrides(): void {\n this.update(() => {\n return {\n ...this.state,\n localOverrides: {},\n remoteFeatureFlags: this.#getEffectiveFeatureFlags({\n localOverrides: {},\n }),\n };\n });\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller.d.cts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,wBAAwB,EACzB,kCAAkC;AACnC,OAAO,KAAK,EAAE,0BAA0B,EAAE,kCAAkC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAErD,OAAO,KAAK,EAAE,IAAI,EAAiB,wBAAwB;AAE3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAA0E;AACxH,OAAO,KAAK,EAAE,wCAAwC,EAAE,iEAAgE;AACxH,OAAO,KAAK,EACV,YAAY,EAGb,mDAAkD;AASnD,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAC5D,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAI1D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,CAAC,EAAE,YAAY,CAAC;IAC9B,qBAAqB,CAAC,EAAE,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,0BAA0B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrD,CAAC;AAoDF,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,kCAAkC,GAC1C,yCAAyC,GACzC,wCAAwC,CAAC;AAE7C,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,oCAAoC,GAAG,SAAS,CAC1D,OAAO,cAAc,EACrB,kCAAkC,EAClC,iCAAiC,CAClC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAO7F;AAoCD;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAqBC;;;;;;;;;;;;;;;OAeG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,gBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,mBAAwB,GACzB,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,qBAAqB,EAAE,MAAM,MAAM,CAAC;QACpC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QACrC,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;QACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,mBAAmB,CAAC,EAAE,YAAY,CAAC;KACpC;IAkFD;;;;;;;OAOG;IACG,wBAAwB,CAAC,KAAK,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB5D;;;;;;;;;;OAUG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgN3B;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;IAIf;;;;;OAKG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI;IAepD;;;;OAIG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAe1C;;OAEG;IACH,qBAAqB,IAAI,IAAI;CAW9B"}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { BaseController, ControllerGetStateAction } from "@metamask/base-controller";
|
|
2
|
-
import type { ControllerStateChangeEvent } from "@metamask/base-controller";
|
|
3
|
-
import type { Messenger } from "@metamask/messenger";
|
|
4
|
-
import type { Json } from "@metamask/utils";
|
|
5
|
-
import type { AbstractClientConfigApiService } from "./client-config-api-service/abstract-client-config-api-service.mjs";
|
|
6
|
-
import type { RemoteFeatureFlagControllerMethodActions } from "./remote-feature-flag-controller-method-action-types.mjs";
|
|
7
|
-
import type { FeatureFlags } from "./remote-feature-flag-controller-types.mjs";
|
|
8
|
-
export declare const controllerName = "RemoteFeatureFlagController";
|
|
9
|
-
export declare const DEFAULT_CACHE_DURATION: number;
|
|
10
|
-
export type RemoteFeatureFlagControllerState = {
|
|
11
|
-
remoteFeatureFlags: FeatureFlags;
|
|
12
|
-
localOverrides?: FeatureFlags;
|
|
13
|
-
rawRemoteFeatureFlags?: FeatureFlags;
|
|
14
|
-
cacheTimestamp: number;
|
|
15
|
-
thresholdCache?: Record<string, number>;
|
|
16
|
-
featureFlagThresholdGroups?: Record<string, string>;
|
|
17
|
-
};
|
|
18
|
-
export type RemoteFeatureFlagControllerGetStateAction = ControllerGetStateAction<typeof controllerName, RemoteFeatureFlagControllerState>;
|
|
19
|
-
export type RemoteFeatureFlagControllerActions = RemoteFeatureFlagControllerGetStateAction | RemoteFeatureFlagControllerMethodActions;
|
|
20
|
-
export type RemoteFeatureFlagControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, RemoteFeatureFlagControllerState>;
|
|
21
|
-
export type RemoteFeatureFlagControllerEvents = RemoteFeatureFlagControllerStateChangeEvent;
|
|
22
|
-
export type RemoteFeatureFlagControllerMessenger = Messenger<typeof controllerName, RemoteFeatureFlagControllerActions, RemoteFeatureFlagControllerEvents>;
|
|
23
|
-
/**
|
|
24
|
-
* Returns the default state for the RemoteFeatureFlagController.
|
|
25
|
-
*
|
|
26
|
-
* @returns The default controller state.
|
|
27
|
-
*/
|
|
28
|
-
export declare function getDefaultRemoteFeatureFlagControllerState(): RemoteFeatureFlagControllerState;
|
|
29
|
-
/**
|
|
30
|
-
* The RemoteFeatureFlagController manages the retrieval and caching of remote feature flags.
|
|
31
|
-
* It fetches feature flags from a remote API, caches them, and provides methods to access
|
|
32
|
-
* and manage these flags. The controller ensures that feature flags are refreshed based on
|
|
33
|
-
* a specified interval and handles cases where the controller is disabled or the network is unavailable.
|
|
34
|
-
*/
|
|
35
|
-
export declare class RemoteFeatureFlagController extends BaseController<typeof controllerName, RemoteFeatureFlagControllerState, RemoteFeatureFlagControllerMessenger> {
|
|
36
|
-
#private;
|
|
37
|
-
/**
|
|
38
|
-
* Constructs a new RemoteFeatureFlagController instance.
|
|
39
|
-
*
|
|
40
|
-
* @param options - The controller options.
|
|
41
|
-
* @param options.messenger - The messenger used for communication.
|
|
42
|
-
* @param options.state - The initial state of the controller.
|
|
43
|
-
* @param options.clientConfigApiService - The service instance to fetch remote feature flags.
|
|
44
|
-
* @param options.fetchInterval - The interval in milliseconds before cached flags expire. Defaults to 1 day.
|
|
45
|
-
* @param options.disabled - Determines if the controller should be disabled initially. Defaults to false.
|
|
46
|
-
* @param options.getMetaMetricsId - Returns metaMetricsId.
|
|
47
|
-
* @param options.getCanonicalProfileId - Returns the canonical profile identifier used for threshold flags by default. Must return an empty string, rather than throwing, when the identifier is unavailable.
|
|
48
|
-
* @param options.metaMetricsFlags - Names of feature flags that should use MetaMetrics ID for threshold assignment.
|
|
49
|
-
* @param options.clientVersion - The current client version for version-based feature flag filtering. Must be a valid 3-part SemVer version string.
|
|
50
|
-
* @param options.prevClientVersion - The previous client version for feature flag cache invalidation.
|
|
51
|
-
* @param options.defaultFeatureFlags - Client-side default feature flags used as the lowest-precedence layer under processed remote flags and local overrides. Not persisted.
|
|
52
|
-
*/
|
|
53
|
-
constructor({ messenger, state, clientConfigApiService, fetchInterval, disabled, getMetaMetricsId, getCanonicalProfileId, metaMetricsFlags, clientVersion, prevClientVersion, defaultFeatureFlags, }: {
|
|
54
|
-
messenger: RemoteFeatureFlagControllerMessenger;
|
|
55
|
-
state?: Partial<RemoteFeatureFlagControllerState>;
|
|
56
|
-
clientConfigApiService: AbstractClientConfigApiService;
|
|
57
|
-
getMetaMetricsId: () => string;
|
|
58
|
-
getCanonicalProfileId: () => string;
|
|
59
|
-
metaMetricsFlags?: readonly string[];
|
|
60
|
-
fetchInterval?: number;
|
|
61
|
-
disabled?: boolean;
|
|
62
|
-
clientVersion: string;
|
|
63
|
-
prevClientVersion?: string;
|
|
64
|
-
defaultFeatureFlags?: FeatureFlags;
|
|
65
|
-
});
|
|
66
|
-
/**
|
|
67
|
-
* Retrieves the remote feature flags, fetching from the API if necessary.
|
|
68
|
-
* Uses caching to prevent redundant API calls and handles concurrent fetches.
|
|
69
|
-
*
|
|
70
|
-
* @param force - When `true`, fetch even if the cache has not expired.
|
|
71
|
-
* Defaults to `false`. Has no effect when the controller is disabled.
|
|
72
|
-
* @returns A promise that resolves to the current set of feature flags.
|
|
73
|
-
*/
|
|
74
|
-
updateRemoteFeatureFlags(force?: boolean): Promise<void>;
|
|
75
|
-
/**
|
|
76
|
-
* Computes the effective feature flags, re-deriving the remote layer from the
|
|
77
|
-
* raw flags already in state. Threshold selection needs to await a hash and
|
|
78
|
-
* so cannot run in the constructor, which is why this cannot be part of
|
|
79
|
-
* construction. Clients must call this once after constructing the
|
|
80
|
-
* controller.
|
|
81
|
-
*
|
|
82
|
-
* When there are no persisted raw flags, as on a fresh install or for state
|
|
83
|
-
* persisted before raw flags were stored, the previous session's flags stand
|
|
84
|
-
* in for the remote layer so that nothing is lost.
|
|
85
|
-
*/
|
|
86
|
-
init(): Promise<void>;
|
|
87
|
-
/**
|
|
88
|
-
* Enables the controller, allowing it to make network requests.
|
|
89
|
-
*/
|
|
90
|
-
enable(): void;
|
|
91
|
-
/**
|
|
92
|
-
* Disables the controller, preventing it from making network requests.
|
|
93
|
-
*/
|
|
94
|
-
disable(): void;
|
|
95
|
-
/**
|
|
96
|
-
* Sets a local override for a specific feature flag.
|
|
97
|
-
*
|
|
98
|
-
* @param flagName - The name of the feature flag to override.
|
|
99
|
-
* @param value - The override value for the feature flag.
|
|
100
|
-
*/
|
|
101
|
-
setFlagOverride(flagName: string, value: Json): void;
|
|
102
|
-
/**
|
|
103
|
-
* Clears the local override for a specific feature flag.
|
|
104
|
-
*
|
|
105
|
-
* @param flagName - The name of the feature flag to clear.
|
|
106
|
-
*/
|
|
107
|
-
removeFlagOverride(flagName: string): void;
|
|
108
|
-
/**
|
|
109
|
-
* Clears all local feature flag overrides.
|
|
110
|
-
*/
|
|
111
|
-
clearAllFlagOverrides(): void;
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=remote-feature-flag-controller.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"remote-feature-flag-controller.d.mts","sourceRoot":"","sources":["../src/remote-feature-flag-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,wBAAwB,EACzB,kCAAkC;AACnC,OAAO,KAAK,EAAE,0BAA0B,EAAE,kCAAkC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAErD,OAAO,KAAK,EAAE,IAAI,EAAiB,wBAAwB;AAE3D,OAAO,KAAK,EAAE,8BAA8B,EAAE,2EAA0E;AACxH,OAAO,KAAK,EAAE,wCAAwC,EAAE,iEAAgE;AACxH,OAAO,KAAK,EACV,YAAY,EAGb,mDAAkD;AASnD,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAC5D,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAI1D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB,EAAE,YAAY,CAAC;IACjC,cAAc,CAAC,EAAE,YAAY,CAAC;IAC9B,qBAAqB,CAAC,EAAE,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,0BAA0B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrD,CAAC;AAoDF,MAAM,MAAM,yCAAyC,GACnD,wBAAwB,CACtB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,kCAAkC,GAC1C,yCAAyC,GACzC,wCAAwC,CAAC;AAE7C,MAAM,MAAM,2CAA2C,GACrD,0BAA0B,CACxB,OAAO,cAAc,EACrB,gCAAgC,CACjC,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GAC3C,2CAA2C,CAAC;AAE9C,MAAM,MAAM,oCAAoC,GAAG,SAAS,CAC1D,OAAO,cAAc,EACrB,kCAAkC,EAClC,iCAAiC,CAClC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,0CAA0C,IAAI,gCAAgC,CAO7F;AAoCD;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,cAAc,CAC7D,OAAO,cAAc,EACrB,gCAAgC,EAChC,oCAAoC,CACrC;;IAqBC;;;;;;;;;;;;;;;OAeG;gBACS,EACV,SAAS,EACT,KAAK,EACL,sBAAsB,EACtB,aAAsC,EACtC,QAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,gBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,mBAAwB,GACzB,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,qBAAqB,EAAE,MAAM,MAAM,CAAC;QACpC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QACrC,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;QACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,mBAAmB,CAAC,EAAE,YAAY,CAAC;KACpC;IAkFD;;;;;;;OAOG;IACG,wBAAwB,CAAC,KAAK,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB5D;;;;;;;;;;OAUG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgN3B;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;IAIf;;;;;OAKG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI;IAepD;;;;OAIG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAe1C;;OAEG;IACH,qBAAqB,IAAI,IAAI;CAW9B"}
|