@firebase/remote-config 0.7.0 → 0.8.0-20260114160934
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/dist/esm/index.esm.js +85 -6
- package/dist/esm/index.esm.js.map +1 -1
- package/dist/esm/src/abt/experiment.d.ts +13 -0
- package/dist/esm/src/errors.d.ts +5 -1
- package/dist/esm/src/public_types.d.ts +19 -0
- package/dist/esm/src/remote_config.d.ts +11 -1
- package/dist/esm/src/storage/storage.d.ts +3 -1
- package/dist/index.cjs.js +85 -6
- package/dist/index.cjs.js.map +1 -1
- package/dist/remote-config-public.d.ts +20 -0
- package/dist/remote-config.d.ts +20 -0
- package/dist/src/abt/experiment.d.ts +13 -0
- package/dist/src/errors.d.ts +5 -1
- package/dist/src/global_index.d.ts +20 -1
- package/dist/src/public_types.d.ts +19 -0
- package/dist/src/remote_config.d.ts +11 -1
- package/dist/src/storage/storage.d.ts +3 -1
- package/package.json +2 -2
package/dist/esm/index.esm.js
CHANGED
|
@@ -5,7 +5,7 @@ import { LogLevel, Logger } from '@firebase/logger';
|
|
|
5
5
|
import '@firebase/installations';
|
|
6
6
|
|
|
7
7
|
const name = "@firebase/remote-config";
|
|
8
|
-
const version = "0.
|
|
8
|
+
const version = "0.8.0-20260114160934";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @license
|
|
@@ -105,7 +105,8 @@ const ERROR_DESCRIPTION_MAP = {
|
|
|
105
105
|
["stream-error" /* ErrorCode.CONFIG_UPDATE_STREAM_ERROR */]: 'The stream was not able to connect to the backend: {$originalErrorMessage}.',
|
|
106
106
|
["realtime-unavailable" /* ErrorCode.CONFIG_UPDATE_UNAVAILABLE */]: 'The Realtime service is unavailable: {$originalErrorMessage}',
|
|
107
107
|
["update-message-invalid" /* ErrorCode.CONFIG_UPDATE_MESSAGE_INVALID */]: 'The stream invalidation message was unparsable: {$originalErrorMessage}',
|
|
108
|
-
["update-not-fetched" /* ErrorCode.CONFIG_UPDATE_NOT_FETCHED */]: 'Unable to fetch the latest config: {$originalErrorMessage}'
|
|
108
|
+
["update-not-fetched" /* ErrorCode.CONFIG_UPDATE_NOT_FETCHED */]: 'Unable to fetch the latest config: {$originalErrorMessage}',
|
|
109
|
+
["analytics-unavailable" /* ErrorCode.ANALYTICS_UNAVAILABLE */]: 'Connection to Firebase Analytics failed: {$originalErrorMessage}'
|
|
109
110
|
};
|
|
110
111
|
const ERROR_FACTORY = new ErrorFactory('remoteconfig' /* service */, 'Remote Config' /* service name */, ERROR_DESCRIPTION_MAP);
|
|
111
112
|
// Note how this is like typeof/instanceof, but for ErrorCode.
|
|
@@ -162,6 +163,64 @@ class Value {
|
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
165
|
|
|
166
|
+
class Experiment {
|
|
167
|
+
constructor(rc) {
|
|
168
|
+
this.storage = rc._storage;
|
|
169
|
+
this.logger = rc._logger;
|
|
170
|
+
this.analyticsProvider = rc._analyticsProvider;
|
|
171
|
+
}
|
|
172
|
+
async updateActiveExperiments(latestExperiments) {
|
|
173
|
+
const currentActiveExperiments = (await this.storage.getActiveExperiments()) || new Set();
|
|
174
|
+
const experimentInfoMap = this.createExperimentInfoMap(latestExperiments);
|
|
175
|
+
this.addActiveExperiments(experimentInfoMap);
|
|
176
|
+
this.removeInactiveExperiments(currentActiveExperiments, experimentInfoMap);
|
|
177
|
+
return this.storage.setActiveExperiments(new Set(experimentInfoMap.keys()));
|
|
178
|
+
}
|
|
179
|
+
createExperimentInfoMap(latestExperiments) {
|
|
180
|
+
const experimentInfoMap = new Map();
|
|
181
|
+
for (const experiment of latestExperiments) {
|
|
182
|
+
experimentInfoMap.set(experiment.experimentId, experiment);
|
|
183
|
+
}
|
|
184
|
+
return experimentInfoMap;
|
|
185
|
+
}
|
|
186
|
+
addActiveExperiments(experimentInfoMap) {
|
|
187
|
+
const customProperty = {};
|
|
188
|
+
for (const [experimentId, experimentInfo] of experimentInfoMap.entries()) {
|
|
189
|
+
customProperty[`firebase${experimentId}`] = experimentInfo.variantId;
|
|
190
|
+
}
|
|
191
|
+
this.addExperimentToAnalytics(customProperty);
|
|
192
|
+
}
|
|
193
|
+
removeInactiveExperiments(currentActiveExperiments, experimentInfoMap) {
|
|
194
|
+
const customProperty = {};
|
|
195
|
+
for (const experimentId of currentActiveExperiments) {
|
|
196
|
+
if (!experimentInfoMap.has(experimentId)) {
|
|
197
|
+
customProperty[`firebase${experimentId}`] = null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
this.addExperimentToAnalytics(customProperty);
|
|
201
|
+
}
|
|
202
|
+
addExperimentToAnalytics(customProperty) {
|
|
203
|
+
if (Object.keys(customProperty).length === 0) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const analytics = this.analyticsProvider.getImmediate({ optional: true });
|
|
208
|
+
if (analytics) {
|
|
209
|
+
analytics.setUserProperties(customProperty);
|
|
210
|
+
analytics.logEvent(`set_firebase_experiment_state`);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
this.logger.warn(`Analytics import failed. Verify if you have imported Firebase Analytics in your app code.`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
throw ERROR_FACTORY.create("analytics-unavailable" /* ErrorCode.ANALYTICS_UNAVAILABLE */, {
|
|
218
|
+
originalErrorMessage: error?.message
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
165
224
|
/**
|
|
166
225
|
* @license
|
|
167
226
|
* Copyright 2020 Google LLC
|
|
@@ -239,10 +298,15 @@ async function activate(remoteConfig) {
|
|
|
239
298
|
// config.
|
|
240
299
|
return false;
|
|
241
300
|
}
|
|
301
|
+
const experiment = new Experiment(rc);
|
|
302
|
+
const updateActiveExperiments = lastSuccessfulFetchResponse.experiments
|
|
303
|
+
? experiment.updateActiveExperiments(lastSuccessfulFetchResponse.experiments)
|
|
304
|
+
: Promise.resolve();
|
|
242
305
|
await Promise.all([
|
|
243
306
|
rc._storageCache.setActiveConfig(lastSuccessfulFetchResponse.config),
|
|
244
307
|
rc._storage.setActiveConfigEtag(lastSuccessfulFetchResponse.eTag),
|
|
245
|
-
rc._storage.setActiveConfigTemplateVersion(lastSuccessfulFetchResponse.templateVersion)
|
|
308
|
+
rc._storage.setActiveConfigTemplateVersion(lastSuccessfulFetchResponse.templateVersion),
|
|
309
|
+
updateActiveExperiments
|
|
246
310
|
]);
|
|
247
311
|
return true;
|
|
248
312
|
}
|
|
@@ -697,6 +761,7 @@ class RestClient {
|
|
|
697
761
|
let config;
|
|
698
762
|
let state;
|
|
699
763
|
let templateVersion;
|
|
764
|
+
let experiments;
|
|
700
765
|
// JSON parsing throws SyntaxError if the response body isn't a JSON string.
|
|
701
766
|
// Requesting application/json and checking for a 200 ensures there's JSON data.
|
|
702
767
|
if (response.status === 200) {
|
|
@@ -712,6 +777,7 @@ class RestClient {
|
|
|
712
777
|
config = responseBody['entries'];
|
|
713
778
|
state = responseBody['state'];
|
|
714
779
|
templateVersion = responseBody['templateVersion'];
|
|
780
|
+
experiments = responseBody['experimentDescriptions'];
|
|
715
781
|
}
|
|
716
782
|
// Normalizes based on legacy state.
|
|
717
783
|
if (state === 'INSTANCE_STATE_UNSPECIFIED') {
|
|
@@ -723,6 +789,7 @@ class RestClient {
|
|
|
723
789
|
else if (state === 'NO_TEMPLATE' || state === 'EMPTY_CONFIG') {
|
|
724
790
|
// These cases can be fixed remotely, so normalize to safe value.
|
|
725
791
|
config = {};
|
|
792
|
+
experiments = [];
|
|
726
793
|
}
|
|
727
794
|
// Normalize to exception-based control flow for non-success cases.
|
|
728
795
|
// Encapsulates HTTP specifics in this class as much as possible. Status is still the best for
|
|
@@ -733,7 +800,7 @@ class RestClient {
|
|
|
733
800
|
httpStatus: status
|
|
734
801
|
});
|
|
735
802
|
}
|
|
736
|
-
return { status, eTag: responseEtag, config, templateVersion };
|
|
803
|
+
return { status, eTag: responseEtag, config, templateVersion, experiments };
|
|
737
804
|
}
|
|
738
805
|
}
|
|
739
806
|
|
|
@@ -899,13 +966,18 @@ class RemoteConfig {
|
|
|
899
966
|
/**
|
|
900
967
|
* @internal
|
|
901
968
|
*/
|
|
902
|
-
_realtimeHandler
|
|
969
|
+
_realtimeHandler,
|
|
970
|
+
/**
|
|
971
|
+
* @internal
|
|
972
|
+
*/
|
|
973
|
+
_analyticsProvider) {
|
|
903
974
|
this.app = app;
|
|
904
975
|
this._client = _client;
|
|
905
976
|
this._storageCache = _storageCache;
|
|
906
977
|
this._storage = _storage;
|
|
907
978
|
this._logger = _logger;
|
|
908
979
|
this._realtimeHandler = _realtimeHandler;
|
|
980
|
+
this._analyticsProvider = _analyticsProvider;
|
|
909
981
|
/**
|
|
910
982
|
* Tracks completion of initialization promise.
|
|
911
983
|
* @internal
|
|
@@ -1026,6 +1098,12 @@ class Storage {
|
|
|
1026
1098
|
setActiveConfigEtag(etag) {
|
|
1027
1099
|
return this.set('active_config_etag', etag);
|
|
1028
1100
|
}
|
|
1101
|
+
getActiveExperiments() {
|
|
1102
|
+
return this.get('active_experiments');
|
|
1103
|
+
}
|
|
1104
|
+
setActiveExperiments(experiments) {
|
|
1105
|
+
return this.set('active_experiments', experiments);
|
|
1106
|
+
}
|
|
1029
1107
|
getThrottleMetadata() {
|
|
1030
1108
|
return this.get('throttle_metadata');
|
|
1031
1109
|
}
|
|
@@ -2016,6 +2094,7 @@ function registerRemoteConfig() {
|
|
|
2016
2094
|
const installations = container
|
|
2017
2095
|
.getProvider('installations-internal')
|
|
2018
2096
|
.getImmediate();
|
|
2097
|
+
const analyticsProvider = container.getProvider('analytics-internal');
|
|
2019
2098
|
// Normalizes optional inputs.
|
|
2020
2099
|
const { projectId, apiKey, appId } = app.options;
|
|
2021
2100
|
if (!projectId) {
|
|
@@ -2042,7 +2121,7 @@ function registerRemoteConfig() {
|
|
|
2042
2121
|
const retryingClient = new RetryingClient(restClient, storage);
|
|
2043
2122
|
const cachingClient = new CachingClient(retryingClient, storage, storageCache, logger);
|
|
2044
2123
|
const realtimeHandler = new RealtimeHandler(installations, storage, SDK_VERSION, namespace, projectId, apiKey, appId, logger, storageCache, cachingClient);
|
|
2045
|
-
const remoteConfigInstance = new RemoteConfig(app, cachingClient, storageCache, storage, logger, realtimeHandler);
|
|
2124
|
+
const remoteConfigInstance = new RemoteConfig(app, cachingClient, storageCache, storage, logger, realtimeHandler, analyticsProvider);
|
|
2046
2125
|
// Starts warming cache.
|
|
2047
2126
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2048
2127
|
ensureInitialized(remoteConfigInstance);
|