@firebase/remote-config 0.5.0-canary.d8aabaf9e → 0.5.0-canary.dcfb3da2e

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 (45) hide show
  1. package/dist/esm/index.esm2017.js +96 -45
  2. package/dist/esm/index.esm2017.js.map +1 -1
  3. package/dist/esm/src/api.d.ts +4 -2
  4. package/dist/esm/src/client/caching_client.d.ts +2 -1
  5. package/dist/esm/src/client/remote_config_fetch_client.d.ts +1 -38
  6. package/dist/esm/src/client/rest_client.d.ts +2 -1
  7. package/dist/esm/src/client/retrying_client.d.ts +2 -1
  8. package/dist/esm/src/errors.d.ts +1 -0
  9. package/dist/esm/src/public_types.d.ts +52 -0
  10. package/dist/esm/src/remote_config.d.ts +1 -1
  11. package/dist/esm/src/storage/storage.d.ts +25 -12
  12. package/dist/esm/src/storage/storage_cache.d.ts +1 -1
  13. package/dist/index.cjs.js +95 -44
  14. package/dist/index.cjs.js.map +1 -1
  15. package/dist/remote-config-public.d.ts +58 -1
  16. package/dist/remote-config.d.ts +58 -1
  17. package/dist/src/api.d.ts +4 -2
  18. package/dist/src/client/caching_client.d.ts +2 -1
  19. package/dist/src/client/remote_config_fetch_client.d.ts +1 -38
  20. package/dist/src/client/rest_client.d.ts +2 -1
  21. package/dist/src/client/retrying_client.d.ts +2 -1
  22. package/dist/src/errors.d.ts +1 -0
  23. package/dist/src/public_types.d.ts +52 -0
  24. package/dist/src/remote_config.d.ts +1 -1
  25. package/dist/src/storage/storage.d.ts +25 -12
  26. package/dist/src/storage/storage_cache.d.ts +1 -1
  27. package/package.json +9 -9
  28. package/dist/esm/test/client/caching_client.test.d.ts +0 -17
  29. package/dist/esm/test/client/rest_client.test.d.ts +0 -17
  30. package/dist/esm/test/client/retrying_client.test.d.ts +0 -17
  31. package/dist/esm/test/errors.test.d.ts +0 -17
  32. package/dist/esm/test/language.test.d.ts +0 -17
  33. package/dist/esm/test/remote_config.test.d.ts +0 -17
  34. package/dist/esm/test/storage/storage.test.d.ts +0 -17
  35. package/dist/esm/test/storage/storage_cache.test.d.ts +0 -17
  36. package/dist/esm/test/value.test.d.ts +0 -17
  37. package/dist/test/client/caching_client.test.d.ts +0 -17
  38. package/dist/test/client/rest_client.test.d.ts +0 -17
  39. package/dist/test/client/retrying_client.test.d.ts +0 -17
  40. package/dist/test/errors.test.d.ts +0 -17
  41. package/dist/test/language.test.d.ts +0 -17
  42. package/dist/test/remote_config.test.d.ts +0 -17
  43. package/dist/test/storage/storage.test.d.ts +0 -17
  44. package/dist/test/storage/storage_cache.test.d.ts +0 -17
  45. package/dist/test/value.test.d.ts +0 -17
@@ -46,6 +46,58 @@ export interface RemoteConfig {
46
46
  */
47
47
  lastFetchStatus: FetchStatus;
48
48
  }
49
+ /**
50
+ * Defines a self-descriptive reference for config key-value pairs.
51
+ */
52
+ export interface FirebaseRemoteConfigObject {
53
+ [key: string]: string;
54
+ }
55
+ /**
56
+ * Defines a successful response (200 or 304).
57
+ *
58
+ * <p>Modeled after the native `Response` interface, but simplified for Remote Config's
59
+ * use case.
60
+ */
61
+ export interface FetchResponse {
62
+ /**
63
+ * The HTTP status, which is useful for differentiating success responses with data from
64
+ * those without.
65
+ *
66
+ * <p>The Remote Config client is modeled after the native `Fetch` interface, so
67
+ * HTTP status is first-class.
68
+ *
69
+ * <p>Disambiguation: the fetch response returns a legacy "state" value that is redundant with the
70
+ * HTTP status code. The former is normalized into the latter.
71
+ */
72
+ status: number;
73
+ /**
74
+ * Defines the ETag response header value.
75
+ *
76
+ * <p>Only defined for 200 and 304 responses.
77
+ */
78
+ eTag?: string;
79
+ /**
80
+ * Defines the map of parameters returned as "entries" in the fetch response body.
81
+ *
82
+ * <p>Only defined for 200 responses.
83
+ */
84
+ config?: FirebaseRemoteConfigObject;
85
+ }
86
+ /**
87
+ * Options for Remote Config initialization.
88
+ *
89
+ * @public
90
+ */
91
+ export interface RemoteConfigOptions {
92
+ /**
93
+ * The ID of the template to use. If not provided, defaults to "firebase".
94
+ */
95
+ templateId?: string;
96
+ /**
97
+ * Hydrates the state with an initial fetch response.
98
+ */
99
+ initialFetchResponse?: FetchResponse;
100
+ }
49
101
  /**
50
102
  * Indicates the source of a value.
51
103
  *
@@ -23,7 +23,7 @@ import { Logger } from '@firebase/logger';
23
23
  /**
24
24
  * Encapsulates business logic mapping network and storage dependencies to the public SDK API.
25
25
  *
26
- * See {@link https://github.com/firebase/firebase-js-sdk/blob/main/packages/firebase/index.d.ts|interface documentation} for method descriptions.
26
+ * See {@link https://github.com/firebase/firebase-js-sdk/blob/main/packages/firebase/compat/index.d.ts|interface documentation} for method descriptions.
27
27
  */
28
28
  export declare class RemoteConfig implements RemoteConfigType {
29
29
  readonly app: FirebaseApp;
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import { FetchStatus, CustomSignals } from '@firebase/remote-config-types';
18
- import { FetchResponse, FirebaseRemoteConfigObject } from '../client/remote_config_fetch_client';
18
+ import { FetchResponse, FirebaseRemoteConfigObject } from '../public_types';
19
19
  /**
20
20
  * A general-purpose store keyed by app + namespace + {@link
21
21
  * ProjectNamespaceKeyFieldValue}.
@@ -44,17 +44,7 @@ export declare function openDatabase(): Promise<IDBDatabase>;
44
44
  /**
45
45
  * Abstracts data persistence.
46
46
  */
47
- export declare class Storage {
48
- private readonly appId;
49
- private readonly appName;
50
- private readonly namespace;
51
- private readonly openDbPromise;
52
- /**
53
- * @param appId enables storage segmentation by app (ID + name).
54
- * @param appName enables storage segmentation by app (ID + name).
55
- * @param namespace enables storage segmentation by namespace.
56
- */
57
- constructor(appId: string, appName: string, namespace: string, openDbPromise?: Promise<IDBDatabase>);
47
+ export declare abstract class Storage {
58
48
  getLastFetchStatus(): Promise<FetchStatus | undefined>;
59
49
  setLastFetchStatus(status: FetchStatus): Promise<void>;
60
50
  getLastSuccessfulFetchTimestampMillis(): Promise<number | undefined>;
@@ -69,6 +59,22 @@ export declare class Storage {
69
59
  setThrottleMetadata(metadata: ThrottleMetadata): Promise<void>;
70
60
  deleteThrottleMetadata(): Promise<void>;
71
61
  getCustomSignals(): Promise<CustomSignals | undefined>;
62
+ abstract setCustomSignals(customSignals: CustomSignals): Promise<CustomSignals>;
63
+ abstract get<T>(key: ProjectNamespaceKeyFieldValue): Promise<T | undefined>;
64
+ abstract set<T>(key: ProjectNamespaceKeyFieldValue, value: T): Promise<void>;
65
+ abstract delete(key: ProjectNamespaceKeyFieldValue): Promise<void>;
66
+ }
67
+ export declare class IndexedDbStorage extends Storage {
68
+ private readonly appId;
69
+ private readonly appName;
70
+ private readonly namespace;
71
+ private readonly openDbPromise;
72
+ /**
73
+ * @param appId enables storage segmentation by app (ID + name).
74
+ * @param appName enables storage segmentation by app (ID + name).
75
+ * @param namespace enables storage segmentation by namespace.
76
+ */
77
+ constructor(appId: string, appName: string, namespace: string, openDbPromise?: Promise<IDBDatabase>);
72
78
  setCustomSignals(customSignals: CustomSignals): Promise<CustomSignals>;
73
79
  /**
74
80
  * Gets a value from the database using the provided transaction.
@@ -92,4 +98,11 @@ export declare class Storage {
92
98
  delete(key: ProjectNamespaceKeyFieldValue): Promise<void>;
93
99
  createCompositeKey(key: ProjectNamespaceKeyFieldValue): string;
94
100
  }
101
+ export declare class InMemoryStorage extends Storage {
102
+ private storage;
103
+ get<T>(key: ProjectNamespaceKeyFieldValue): Promise<T>;
104
+ set<T>(key: ProjectNamespaceKeyFieldValue, value: T): Promise<void>;
105
+ delete(key: ProjectNamespaceKeyFieldValue): Promise<void>;
106
+ setCustomSignals(customSignals: CustomSignals): Promise<CustomSignals>;
107
+ }
95
108
  export {};
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import { FetchStatus, CustomSignals } from '@firebase/remote-config-types';
18
- import { FirebaseRemoteConfigObject } from '../client/remote_config_fetch_client';
18
+ import { FirebaseRemoteConfigObject } from '../public_types';
19
19
  import { Storage } from './storage';
20
20
  /**
21
21
  * A memory cache layer over storage to support the SDK's synchronous read requirements.
package/dist/index.cjs.js CHANGED
@@ -9,7 +9,7 @@ var logger = require('@firebase/logger');
9
9
  require('@firebase/installations');
10
10
 
11
11
  const name = "@firebase/remote-config";
12
- const version = "0.5.0-canary.d8aabaf9e";
12
+ const version = "0.5.0-canary.dcfb3da2e";
13
13
 
14
14
  /**
15
15
  * @license
@@ -85,6 +85,7 @@ const RC_CUSTOM_SIGNAL_VALUE_MAX_LENGTH = 500;
85
85
  * limitations under the License.
86
86
  */
87
87
  const ERROR_DESCRIPTION_MAP = {
88
+ ["already-initialized" /* ErrorCode.ALREADY_INITIALIZED */]: 'Remote Config already initialized',
88
89
  ["registration-window" /* ErrorCode.REGISTRATION_WINDOW */]: 'Undefined window object. This SDK only supports usage in a browser environment.',
89
90
  ["registration-project-id" /* ErrorCode.REGISTRATION_PROJECT_ID */]: 'Undefined project identifier. Check Firebase app initialization.',
90
91
  ["registration-api-key" /* ErrorCode.REGISTRATION_API_KEY */]: 'Undefined API key. Check Firebase app initialization.',
@@ -180,14 +181,40 @@ class Value {
180
181
  /**
181
182
  *
182
183
  * @param app - The {@link @firebase/app#FirebaseApp} instance.
184
+ * @param options - Optional. The {@link RemoteConfigOptions} with which to instantiate the
185
+ * Remote Config instance.
183
186
  * @returns A {@link RemoteConfig} instance.
184
187
  *
185
188
  * @public
186
189
  */
187
- function getRemoteConfig(app$1 = app.getApp()) {
190
+ function getRemoteConfig(app$1 = app.getApp(), options = {}) {
191
+ var _a, _b;
188
192
  app$1 = util.getModularInstance(app$1);
189
193
  const rcProvider = app._getProvider(app$1, RC_COMPONENT_NAME);
190
- return rcProvider.getImmediate();
194
+ if (rcProvider.isInitialized()) {
195
+ const initialOptions = rcProvider.getOptions();
196
+ if (util.deepEqual(initialOptions, options)) {
197
+ return rcProvider.getImmediate();
198
+ }
199
+ throw ERROR_FACTORY.create("already-initialized" /* ErrorCode.ALREADY_INITIALIZED */);
200
+ }
201
+ rcProvider.initialize({ options });
202
+ const rc = rcProvider.getImmediate();
203
+ if (options.initialFetchResponse) {
204
+ // We use these initial writes as the initialization promise since they will hydrate the same
205
+ // fields that `storageCache.loadFromStorage` would set.
206
+ rc._initializePromise = Promise.all([
207
+ rc._storage.setLastSuccessfulFetchResponse(options.initialFetchResponse),
208
+ rc._storage.setActiveConfigEtag(((_a = options.initialFetchResponse) === null || _a === void 0 ? void 0 : _a.eTag) || ''),
209
+ rc._storageCache.setLastSuccessfulFetchTimestampMillis(Date.now()),
210
+ rc._storageCache.setLastFetchStatus('success'),
211
+ rc._storageCache.setActiveConfig(((_b = options.initialFetchResponse) === null || _b === void 0 ? void 0 : _b.config) || {})
212
+ ]).then();
213
+ // The `storageCache` methods above set their in-memory fields synchronously, so it's
214
+ // safe to declare our initialization complete at this point.
215
+ rc._isInitializationComplete = true;
216
+ }
217
+ return rc;
191
218
  }
192
219
  /**
193
220
  * Makes the last fetched config available to the getters.
@@ -809,7 +836,7 @@ const DEFAULT_CACHE_MAX_AGE_MILLIS = 12 * 60 * 60 * 1000; // Twelve hours.
809
836
  /**
810
837
  * Encapsulates business logic mapping network and storage dependencies to the public SDK API.
811
838
  *
812
- * See {@link https://github.com/firebase/firebase-js-sdk/blob/main/packages/firebase/index.d.ts|interface documentation} for method descriptions.
839
+ * See {@link https://github.com/firebase/firebase-js-sdk/blob/main/packages/firebase/compat/index.d.ts|interface documentation} for method descriptions.
813
840
  */
814
841
  class RemoteConfig {
815
842
  get fetchTimeMillis() {
@@ -933,17 +960,6 @@ function openDatabase() {
933
960
  * Abstracts data persistence.
934
961
  */
935
962
  class Storage {
936
- /**
937
- * @param appId enables storage segmentation by app (ID + name).
938
- * @param appName enables storage segmentation by app (ID + name).
939
- * @param namespace enables storage segmentation by namespace.
940
- */
941
- constructor(appId, appName, namespace, openDbPromise = openDatabase()) {
942
- this.appId = appId;
943
- this.appName = appName;
944
- this.namespace = namespace;
945
- this.openDbPromise = openDbPromise;
946
- }
947
963
  getLastFetchStatus() {
948
964
  return this.get('last_fetch_status');
949
965
  }
@@ -988,28 +1004,25 @@ class Storage {
988
1004
  getCustomSignals() {
989
1005
  return this.get('custom_signals');
990
1006
  }
1007
+ }
1008
+ class IndexedDbStorage extends Storage {
1009
+ /**
1010
+ * @param appId enables storage segmentation by app (ID + name).
1011
+ * @param appName enables storage segmentation by app (ID + name).
1012
+ * @param namespace enables storage segmentation by namespace.
1013
+ */
1014
+ constructor(appId, appName, namespace, openDbPromise = openDatabase()) {
1015
+ super();
1016
+ this.appId = appId;
1017
+ this.appName = appName;
1018
+ this.namespace = namespace;
1019
+ this.openDbPromise = openDbPromise;
1020
+ }
991
1021
  async setCustomSignals(customSignals) {
992
1022
  const db = await this.openDbPromise;
993
1023
  const transaction = db.transaction([APP_NAMESPACE_STORE], 'readwrite');
994
1024
  const storedSignals = await this.getWithTransaction('custom_signals', transaction);
995
- const combinedSignals = Object.assign(Object.assign({}, storedSignals), customSignals);
996
- // Filter out key-value assignments with null values since they are signals being unset
997
- const updatedSignals = Object.fromEntries(Object.entries(combinedSignals)
998
- .filter(([_, v]) => v !== null)
999
- .map(([k, v]) => {
1000
- // Stringify numbers to store a map of string keys and values which can be sent
1001
- // as-is in a fetch call.
1002
- if (typeof v === 'number') {
1003
- return [k, v.toString()];
1004
- }
1005
- return [k, v];
1006
- }));
1007
- // Throw an error if the number of custom signals to be stored exceeds the limit
1008
- if (Object.keys(updatedSignals).length > RC_CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS) {
1009
- throw ERROR_FACTORY.create("custom-signal-max-allowed-signals" /* ErrorCode.CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS */, {
1010
- maxSignals: RC_CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS
1011
- });
1012
- }
1025
+ const updatedSignals = mergeCustomSignals(customSignals, storedSignals || {});
1013
1026
  await this.setWithTransaction('custom_signals', updatedSignals, transaction);
1014
1027
  return updatedSignals;
1015
1028
  }
@@ -1114,6 +1127,50 @@ class Storage {
1114
1127
  return [this.appId, this.appName, this.namespace, key].join();
1115
1128
  }
1116
1129
  }
1130
+ class InMemoryStorage extends Storage {
1131
+ constructor() {
1132
+ super(...arguments);
1133
+ this.storage = {};
1134
+ }
1135
+ async get(key) {
1136
+ return Promise.resolve(this.storage[key]);
1137
+ }
1138
+ async set(key, value) {
1139
+ this.storage[key] = value;
1140
+ return Promise.resolve(undefined);
1141
+ }
1142
+ async delete(key) {
1143
+ this.storage[key] = undefined;
1144
+ return Promise.resolve();
1145
+ }
1146
+ async setCustomSignals(customSignals) {
1147
+ const storedSignals = (this.storage['custom_signals'] ||
1148
+ {});
1149
+ this.storage['custom_signals'] = mergeCustomSignals(customSignals, storedSignals);
1150
+ return Promise.resolve(this.storage['custom_signals']);
1151
+ }
1152
+ }
1153
+ function mergeCustomSignals(customSignals, storedSignals) {
1154
+ const combinedSignals = Object.assign(Object.assign({}, storedSignals), customSignals);
1155
+ // Filter out key-value assignments with null values since they are signals being unset
1156
+ const updatedSignals = Object.fromEntries(Object.entries(combinedSignals)
1157
+ .filter(([_, v]) => v !== null)
1158
+ .map(([k, v]) => {
1159
+ // Stringify numbers to store a map of string keys and values which can be sent
1160
+ // as-is in a fetch call.
1161
+ if (typeof v === 'number') {
1162
+ return [k, v.toString()];
1163
+ }
1164
+ return [k, v];
1165
+ }));
1166
+ // Throw an error if the number of custom signals to be stored exceeds the limit
1167
+ if (Object.keys(updatedSignals).length > RC_CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS) {
1168
+ throw ERROR_FACTORY.create("custom-signal-max-allowed-signals" /* ErrorCode.CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS */, {
1169
+ maxSignals: RC_CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS
1170
+ });
1171
+ }
1172
+ return updatedSignals;
1173
+ }
1117
1174
 
1118
1175
  /**
1119
1176
  * @license
@@ -1225,7 +1282,7 @@ function registerRemoteConfig() {
1225
1282
  app.registerVersion(name, version);
1226
1283
  // BUILD_TARGET will be replaced by values like esm2017, cjs2017, etc during the compilation
1227
1284
  app.registerVersion(name, version, 'cjs2017');
1228
- function remoteConfigFactory(container, { instanceIdentifier: namespace }) {
1285
+ function remoteConfigFactory(container, { options }) {
1229
1286
  /* Dependencies */
1230
1287
  // getImmediate for FirebaseApp will always succeed
1231
1288
  const app$1 = container.getProvider('app').getImmediate();
@@ -1233,14 +1290,6 @@ function registerRemoteConfig() {
1233
1290
  const installations = container
1234
1291
  .getProvider('installations-internal')
1235
1292
  .getImmediate();
1236
- // Guards against the SDK being used in non-browser environments.
1237
- if (typeof window === 'undefined') {
1238
- throw ERROR_FACTORY.create("registration-window" /* ErrorCode.REGISTRATION_WINDOW */);
1239
- }
1240
- // Guards against the SDK being used when indexedDB is not available.
1241
- if (!util.isIndexedDBAvailable()) {
1242
- throw ERROR_FACTORY.create("indexed-db-unavailable" /* ErrorCode.INDEXED_DB_UNAVAILABLE */);
1243
- }
1244
1293
  // Normalizes optional inputs.
1245
1294
  const { projectId, apiKey, appId } = app$1.options;
1246
1295
  if (!projectId) {
@@ -1252,8 +1301,10 @@ function registerRemoteConfig() {
1252
1301
  if (!appId) {
1253
1302
  throw ERROR_FACTORY.create("registration-app-id" /* ErrorCode.REGISTRATION_APP_ID */);
1254
1303
  }
1255
- namespace = namespace || 'firebase';
1256
- const storage = new Storage(appId, app$1.name, namespace);
1304
+ const namespace = (options === null || options === void 0 ? void 0 : options.templateId) || 'firebase';
1305
+ const storage = util.isIndexedDBAvailable()
1306
+ ? new IndexedDbStorage(appId, app$1.name, namespace)
1307
+ : new InMemoryStorage();
1257
1308
  const storageCache = new StorageCache(storage);
1258
1309
  const logger$1 = new logger.Logger(name);
1259
1310
  // Sets ERROR as the default log level.