GSI - Employe Self Service Mobile
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1397 lines
60 KiB

2 months ago
  1. import { getApp, _getProvider, _registerComponent, registerVersion, SDK_VERSION } from '@firebase/app';
  2. import { ErrorFactory, FirebaseError, getModularInstance, calculateBackoffMillis, isIndexedDBAvailable, validateIndexedDBOpenable } from '@firebase/util';
  3. import { Component } from '@firebase/component';
  4. import { LogLevel, Logger } from '@firebase/logger';
  5. import { __awaiter, __generator, __assign } from 'tslib';
  6. import '@firebase/installations';
  7. var name = "@firebase/remote-config";
  8. var version = "0.4.1";
  9. /**
  10. * @license
  11. * Copyright 2019 Google LLC
  12. *
  13. * Licensed under the Apache License, Version 2.0 (the "License");
  14. * you may not use this file except in compliance with the License.
  15. * You may obtain a copy of the License at
  16. *
  17. * http://www.apache.org/licenses/LICENSE-2.0
  18. *
  19. * Unless required by applicable law or agreed to in writing, software
  20. * distributed under the License is distributed on an "AS IS" BASIS,
  21. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  22. * See the License for the specific language governing permissions and
  23. * limitations under the License.
  24. */
  25. /**
  26. * Shims a minimal AbortSignal.
  27. *
  28. * <p>AbortController's AbortSignal conveniently decouples fetch timeout logic from other aspects
  29. * of networking, such as retries. Firebase doesn't use AbortController enough to justify a
  30. * polyfill recommendation, like we do with the Fetch API, but this minimal shim can easily be
  31. * swapped out if/when we do.
  32. */
  33. var RemoteConfigAbortSignal = /** @class */ (function () {
  34. function RemoteConfigAbortSignal() {
  35. this.listeners = [];
  36. }
  37. RemoteConfigAbortSignal.prototype.addEventListener = function (listener) {
  38. this.listeners.push(listener);
  39. };
  40. RemoteConfigAbortSignal.prototype.abort = function () {
  41. this.listeners.forEach(function (listener) { return listener(); });
  42. };
  43. return RemoteConfigAbortSignal;
  44. }());
  45. /**
  46. * @license
  47. * Copyright 2020 Google LLC
  48. *
  49. * Licensed under the Apache License, Version 2.0 (the "License");
  50. * you may not use this file except in compliance with the License.
  51. * You may obtain a copy of the License at
  52. *
  53. * http://www.apache.org/licenses/LICENSE-2.0
  54. *
  55. * Unless required by applicable law or agreed to in writing, software
  56. * distributed under the License is distributed on an "AS IS" BASIS,
  57. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  58. * See the License for the specific language governing permissions and
  59. * limitations under the License.
  60. */
  61. var RC_COMPONENT_NAME = 'remote-config';
  62. /**
  63. * @license
  64. * Copyright 2019 Google LLC
  65. *
  66. * Licensed under the Apache License, Version 2.0 (the "License");
  67. * you may not use this file except in compliance with the License.
  68. * You may obtain a copy of the License at
  69. *
  70. * http://www.apache.org/licenses/LICENSE-2.0
  71. *
  72. * Unless required by applicable law or agreed to in writing, software
  73. * distributed under the License is distributed on an "AS IS" BASIS,
  74. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  75. * See the License for the specific language governing permissions and
  76. * limitations under the License.
  77. */
  78. var _a;
  79. var ERROR_DESCRIPTION_MAP = (_a = {},
  80. _a["registration-window" /* ErrorCode.REGISTRATION_WINDOW */] = 'Undefined window object. This SDK only supports usage in a browser environment.',
  81. _a["registration-project-id" /* ErrorCode.REGISTRATION_PROJECT_ID */] = 'Undefined project identifier. Check Firebase app initialization.',
  82. _a["registration-api-key" /* ErrorCode.REGISTRATION_API_KEY */] = 'Undefined API key. Check Firebase app initialization.',
  83. _a["registration-app-id" /* ErrorCode.REGISTRATION_APP_ID */] = 'Undefined app identifier. Check Firebase app initialization.',
  84. _a["storage-open" /* ErrorCode.STORAGE_OPEN */] = 'Error thrown when opening storage. Original error: {$originalErrorMessage}.',
  85. _a["storage-get" /* ErrorCode.STORAGE_GET */] = 'Error thrown when reading from storage. Original error: {$originalErrorMessage}.',
  86. _a["storage-set" /* ErrorCode.STORAGE_SET */] = 'Error thrown when writing to storage. Original error: {$originalErrorMessage}.',
  87. _a["storage-delete" /* ErrorCode.STORAGE_DELETE */] = 'Error thrown when deleting from storage. Original error: {$originalErrorMessage}.',
  88. _a["fetch-client-network" /* ErrorCode.FETCH_NETWORK */] = 'Fetch client failed to connect to a network. Check Internet connection.' +
  89. ' Original error: {$originalErrorMessage}.',
  90. _a["fetch-timeout" /* ErrorCode.FETCH_TIMEOUT */] = 'The config fetch request timed out. ' +
  91. ' Configure timeout using "fetchTimeoutMillis" SDK setting.',
  92. _a["fetch-throttle" /* ErrorCode.FETCH_THROTTLE */] = 'The config fetch request timed out while in an exponential backoff state.' +
  93. ' Configure timeout using "fetchTimeoutMillis" SDK setting.' +
  94. ' Unix timestamp in milliseconds when fetch request throttling ends: {$throttleEndTimeMillis}.',
  95. _a["fetch-client-parse" /* ErrorCode.FETCH_PARSE */] = 'Fetch client could not parse response.' +
  96. ' Original error: {$originalErrorMessage}.',
  97. _a["fetch-status" /* ErrorCode.FETCH_STATUS */] = 'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.',
  98. _a["indexed-db-unavailable" /* ErrorCode.INDEXED_DB_UNAVAILABLE */] = 'Indexed DB is not supported by current browser',
  99. _a);
  100. var ERROR_FACTORY = new ErrorFactory('remoteconfig' /* service */, 'Remote Config' /* service name */, ERROR_DESCRIPTION_MAP);
  101. // Note how this is like typeof/instanceof, but for ErrorCode.
  102. function hasErrorCode(e, errorCode) {
  103. return e instanceof FirebaseError && e.code.indexOf(errorCode) !== -1;
  104. }
  105. /**
  106. * @license
  107. * Copyright 2019 Google LLC
  108. *
  109. * Licensed under the Apache License, Version 2.0 (the "License");
  110. * you may not use this file except in compliance with the License.
  111. * You may obtain a copy of the License at
  112. *
  113. * http://www.apache.org/licenses/LICENSE-2.0
  114. *
  115. * Unless required by applicable law or agreed to in writing, software
  116. * distributed under the License is distributed on an "AS IS" BASIS,
  117. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  118. * See the License for the specific language governing permissions and
  119. * limitations under the License.
  120. */
  121. var DEFAULT_VALUE_FOR_BOOLEAN = false;
  122. var DEFAULT_VALUE_FOR_STRING = '';
  123. var DEFAULT_VALUE_FOR_NUMBER = 0;
  124. var BOOLEAN_TRUTHY_VALUES = ['1', 'true', 't', 'yes', 'y', 'on'];
  125. var Value = /** @class */ (function () {
  126. function Value(_source, _value) {
  127. if (_value === void 0) { _value = DEFAULT_VALUE_FOR_STRING; }
  128. this._source = _source;
  129. this._value = _value;
  130. }
  131. Value.prototype.asString = function () {
  132. return this._value;
  133. };
  134. Value.prototype.asBoolean = function () {
  135. if (this._source === 'static') {
  136. return DEFAULT_VALUE_FOR_BOOLEAN;
  137. }
  138. return BOOLEAN_TRUTHY_VALUES.indexOf(this._value.toLowerCase()) >= 0;
  139. };
  140. Value.prototype.asNumber = function () {
  141. if (this._source === 'static') {
  142. return DEFAULT_VALUE_FOR_NUMBER;
  143. }
  144. var num = Number(this._value);
  145. if (isNaN(num)) {
  146. num = DEFAULT_VALUE_FOR_NUMBER;
  147. }
  148. return num;
  149. };
  150. Value.prototype.getSource = function () {
  151. return this._source;
  152. };
  153. return Value;
  154. }());
  155. /**
  156. * @license
  157. * Copyright 2020 Google LLC
  158. *
  159. * Licensed under the Apache License, Version 2.0 (the "License");
  160. * you may not use this file except in compliance with the License.
  161. * You may obtain a copy of the License at
  162. *
  163. * http://www.apache.org/licenses/LICENSE-2.0
  164. *
  165. * Unless required by applicable law or agreed to in writing, software
  166. * distributed under the License is distributed on an "AS IS" BASIS,
  167. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  168. * See the License for the specific language governing permissions and
  169. * limitations under the License.
  170. */
  171. /**
  172. *
  173. * @param app - The {@link @firebase/app#FirebaseApp} instance.
  174. * @returns A {@link RemoteConfig} instance.
  175. *
  176. * @public
  177. */
  178. function getRemoteConfig(app) {
  179. if (app === void 0) { app = getApp(); }
  180. app = getModularInstance(app);
  181. var rcProvider = _getProvider(app, RC_COMPONENT_NAME);
  182. return rcProvider.getImmediate();
  183. }
  184. /**
  185. * Makes the last fetched config available to the getters.
  186. * @param remoteConfig - The {@link RemoteConfig} instance.
  187. * @returns A `Promise` which resolves to true if the current call activated the fetched configs.
  188. * If the fetched configs were already activated, the `Promise` will resolve to false.
  189. *
  190. * @public
  191. */
  192. function activate(remoteConfig) {
  193. return __awaiter(this, void 0, void 0, function () {
  194. var rc, _a, lastSuccessfulFetchResponse, activeConfigEtag;
  195. return __generator(this, function (_b) {
  196. switch (_b.label) {
  197. case 0:
  198. rc = getModularInstance(remoteConfig);
  199. return [4 /*yield*/, Promise.all([
  200. rc._storage.getLastSuccessfulFetchResponse(),
  201. rc._storage.getActiveConfigEtag()
  202. ])];
  203. case 1:
  204. _a = _b.sent(), lastSuccessfulFetchResponse = _a[0], activeConfigEtag = _a[1];
  205. if (!lastSuccessfulFetchResponse ||
  206. !lastSuccessfulFetchResponse.config ||
  207. !lastSuccessfulFetchResponse.eTag ||
  208. lastSuccessfulFetchResponse.eTag === activeConfigEtag) {
  209. // Either there is no successful fetched config, or is the same as current active
  210. // config.
  211. return [2 /*return*/, false];
  212. }
  213. return [4 /*yield*/, Promise.all([
  214. rc._storageCache.setActiveConfig(lastSuccessfulFetchResponse.config),
  215. rc._storage.setActiveConfigEtag(lastSuccessfulFetchResponse.eTag)
  216. ])];
  217. case 2:
  218. _b.sent();
  219. return [2 /*return*/, true];
  220. }
  221. });
  222. });
  223. }
  224. /**
  225. * Ensures the last activated config are available to the getters.
  226. * @param remoteConfig - The {@link RemoteConfig} instance.
  227. *
  228. * @returns A `Promise` that resolves when the last activated config is available to the getters.
  229. * @public
  230. */
  231. function ensureInitialized(remoteConfig) {
  232. var rc = getModularInstance(remoteConfig);
  233. if (!rc._initializePromise) {
  234. rc._initializePromise = rc._storageCache.loadFromStorage().then(function () {
  235. rc._isInitializationComplete = true;
  236. });
  237. }
  238. return rc._initializePromise;
  239. }
  240. /**
  241. * Fetches and caches configuration from the Remote Config service.
  242. * @param remoteConfig - The {@link RemoteConfig} instance.
  243. * @public
  244. */
  245. function fetchConfig(remoteConfig) {
  246. return __awaiter(this, void 0, void 0, function () {
  247. var rc, abortSignal, e_1, lastFetchStatus;
  248. var _this = this;
  249. return __generator(this, function (_a) {
  250. switch (_a.label) {
  251. case 0:
  252. rc = getModularInstance(remoteConfig);
  253. abortSignal = new RemoteConfigAbortSignal();
  254. setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
  255. return __generator(this, function (_a) {
  256. // Note a very low delay, eg < 10ms, can elapse before listeners are initialized.
  257. abortSignal.abort();
  258. return [2 /*return*/];
  259. });
  260. }); }, rc.settings.fetchTimeoutMillis);
  261. _a.label = 1;
  262. case 1:
  263. _a.trys.push([1, 4, , 6]);
  264. return [4 /*yield*/, rc._client.fetch({
  265. cacheMaxAgeMillis: rc.settings.minimumFetchIntervalMillis,
  266. signal: abortSignal
  267. })];
  268. case 2:
  269. _a.sent();
  270. return [4 /*yield*/, rc._storageCache.setLastFetchStatus('success')];
  271. case 3:
  272. _a.sent();
  273. return [3 /*break*/, 6];
  274. case 4:
  275. e_1 = _a.sent();
  276. lastFetchStatus = hasErrorCode(e_1, "fetch-throttle" /* ErrorCode.FETCH_THROTTLE */)
  277. ? 'throttle'
  278. : 'failure';
  279. return [4 /*yield*/, rc._storageCache.setLastFetchStatus(lastFetchStatus)];
  280. case 5:
  281. _a.sent();
  282. throw e_1;
  283. case 6: return [2 /*return*/];
  284. }
  285. });
  286. });
  287. }
  288. /**
  289. * Gets all config.
  290. *
  291. * @param remoteConfig - The {@link RemoteConfig} instance.
  292. * @returns All config.
  293. *
  294. * @public
  295. */
  296. function getAll(remoteConfig) {
  297. var rc = getModularInstance(remoteConfig);
  298. return getAllKeys(rc._storageCache.getActiveConfig(), rc.defaultConfig).reduce(function (allConfigs, key) {
  299. allConfigs[key] = getValue(remoteConfig, key);
  300. return allConfigs;
  301. }, {});
  302. }
  303. /**
  304. * Gets the value for the given key as a boolean.
  305. *
  306. * Convenience method for calling <code>remoteConfig.getValue(key).asBoolean()</code>.
  307. *
  308. * @param remoteConfig - The {@link RemoteConfig} instance.
  309. * @param key - The name of the parameter.
  310. *
  311. * @returns The value for the given key as a boolean.
  312. * @public
  313. */
  314. function getBoolean(remoteConfig, key) {
  315. return getValue(getModularInstance(remoteConfig), key).asBoolean();
  316. }
  317. /**
  318. * Gets the value for the given key as a number.
  319. *
  320. * Convenience method for calling <code>remoteConfig.getValue(key).asNumber()</code>.
  321. *
  322. * @param remoteConfig - The {@link RemoteConfig} instance.
  323. * @param key - The name of the parameter.
  324. *
  325. * @returns The value for the given key as a number.
  326. *
  327. * @public
  328. */
  329. function getNumber(remoteConfig, key) {
  330. return getValue(getModularInstance(remoteConfig), key).asNumber();
  331. }
  332. /**
  333. * Gets the value for the given key as a string.
  334. * Convenience method for calling <code>remoteConfig.getValue(key).asString()</code>.
  335. *
  336. * @param remoteConfig - The {@link RemoteConfig} instance.
  337. * @param key - The name of the parameter.
  338. *
  339. * @returns The value for the given key as a string.
  340. *
  341. * @public
  342. */
  343. function getString(remoteConfig, key) {
  344. return getValue(getModularInstance(remoteConfig), key).asString();
  345. }
  346. /**
  347. * Gets the {@link Value} for the given key.
  348. *
  349. * @param remoteConfig - The {@link RemoteConfig} instance.
  350. * @param key - The name of the parameter.
  351. *
  352. * @returns The value for the given key.
  353. *
  354. * @public
  355. */
  356. function getValue(remoteConfig, key) {
  357. var rc = getModularInstance(remoteConfig);
  358. if (!rc._isInitializationComplete) {
  359. rc._logger.debug("A value was requested for key \"".concat(key, "\" before SDK initialization completed.") +
  360. ' Await on ensureInitialized if the intent was to get a previously activated value.');
  361. }
  362. var activeConfig = rc._storageCache.getActiveConfig();
  363. if (activeConfig && activeConfig[key] !== undefined) {
  364. return new Value('remote', activeConfig[key]);
  365. }
  366. else if (rc.defaultConfig && rc.defaultConfig[key] !== undefined) {
  367. return new Value('default', String(rc.defaultConfig[key]));
  368. }
  369. rc._logger.debug("Returning static value for key \"".concat(key, "\".") +
  370. ' Define a default or remote value if this is unintentional.');
  371. return new Value('static');
  372. }
  373. /**
  374. * Defines the log level to use.
  375. *
  376. * @param remoteConfig - The {@link RemoteConfig} instance.
  377. * @param logLevel - The log level to set.
  378. *
  379. * @public
  380. */
  381. function setLogLevel(remoteConfig, logLevel) {
  382. var rc = getModularInstance(remoteConfig);
  383. switch (logLevel) {
  384. case 'debug':
  385. rc._logger.logLevel = LogLevel.DEBUG;
  386. break;
  387. case 'silent':
  388. rc._logger.logLevel = LogLevel.SILENT;
  389. break;
  390. default:
  391. rc._logger.logLevel = LogLevel.ERROR;
  392. }
  393. }
  394. /**
  395. * Dedupes and returns an array of all the keys of the received objects.
  396. */
  397. function getAllKeys(obj1, obj2) {
  398. if (obj1 === void 0) { obj1 = {}; }
  399. if (obj2 === void 0) { obj2 = {}; }
  400. return Object.keys(__assign(__assign({}, obj1), obj2));
  401. }
  402. /**
  403. * @license
  404. * Copyright 2019 Google LLC
  405. *
  406. * Licensed under the Apache License, Version 2.0 (the "License");
  407. * you may not use this file except in compliance with the License.
  408. * You may obtain a copy of the License at
  409. *
  410. * http://www.apache.org/licenses/LICENSE-2.0
  411. *
  412. * Unless required by applicable law or agreed to in writing, software
  413. * distributed under the License is distributed on an "AS IS" BASIS,
  414. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  415. * See the License for the specific language governing permissions and
  416. * limitations under the License.
  417. */
  418. /**
  419. * Implements the {@link RemoteConfigClient} abstraction with success response caching.
  420. *
  421. * <p>Comparable to the browser's Cache API for responses, but the Cache API requires a Service
  422. * Worker, which requires HTTPS, which would significantly complicate SDK installation. Also, the
  423. * Cache API doesn't support matching entries by time.
  424. */
  425. var CachingClient = /** @class */ (function () {
  426. function CachingClient(client, storage, storageCache, logger) {
  427. this.client = client;
  428. this.storage = storage;
  429. this.storageCache = storageCache;
  430. this.logger = logger;
  431. }
  432. /**
  433. * Returns true if the age of the cached fetched configs is less than or equal to
  434. * {@link Settings#minimumFetchIntervalInSeconds}.
  435. *
  436. * <p>This is comparable to passing `headers = { 'Cache-Control': max-age <maxAge> }` to the
  437. * native Fetch API.
  438. *
  439. * <p>Visible for testing.
  440. */
  441. CachingClient.prototype.isCachedDataFresh = function (cacheMaxAgeMillis, lastSuccessfulFetchTimestampMillis) {
  442. // Cache can only be fresh if it's populated.
  443. if (!lastSuccessfulFetchTimestampMillis) {
  444. this.logger.debug('Config fetch cache check. Cache unpopulated.');
  445. return false;
  446. }
  447. // Calculates age of cache entry.
  448. var cacheAgeMillis = Date.now() - lastSuccessfulFetchTimestampMillis;
  449. var isCachedDataFresh = cacheAgeMillis <= cacheMaxAgeMillis;
  450. this.logger.debug('Config fetch cache check.' +
  451. " Cache age millis: ".concat(cacheAgeMillis, ".") +
  452. " Cache max age millis (minimumFetchIntervalMillis setting): ".concat(cacheMaxAgeMillis, ".") +
  453. " Is cache hit: ".concat(isCachedDataFresh, "."));
  454. return isCachedDataFresh;
  455. };
  456. CachingClient.prototype.fetch = function (request) {
  457. return __awaiter(this, void 0, void 0, function () {
  458. var _a, lastSuccessfulFetchTimestampMillis, lastSuccessfulFetchResponse, response, storageOperations;
  459. return __generator(this, function (_b) {
  460. switch (_b.label) {
  461. case 0: return [4 /*yield*/, Promise.all([
  462. this.storage.getLastSuccessfulFetchTimestampMillis(),
  463. this.storage.getLastSuccessfulFetchResponse()
  464. ])];
  465. case 1:
  466. _a = _b.sent(), lastSuccessfulFetchTimestampMillis = _a[0], lastSuccessfulFetchResponse = _a[1];
  467. // Exits early on cache hit.
  468. if (lastSuccessfulFetchResponse &&
  469. this.isCachedDataFresh(request.cacheMaxAgeMillis, lastSuccessfulFetchTimestampMillis)) {
  470. return [2 /*return*/, lastSuccessfulFetchResponse];
  471. }
  472. // Deviates from pure decorator by not honoring a passed ETag since we don't have a public API
  473. // that allows the caller to pass an ETag.
  474. request.eTag =
  475. lastSuccessfulFetchResponse && lastSuccessfulFetchResponse.eTag;
  476. return [4 /*yield*/, this.client.fetch(request)];
  477. case 2:
  478. response = _b.sent();
  479. storageOperations = [
  480. // Uses write-through cache for consistency with synchronous public API.
  481. this.storageCache.setLastSuccessfulFetchTimestampMillis(Date.now())
  482. ];
  483. if (response.status === 200) {
  484. // Caches response only if it has changed, ie non-304 responses.
  485. storageOperations.push(this.storage.setLastSuccessfulFetchResponse(response));
  486. }
  487. return [4 /*yield*/, Promise.all(storageOperations)];
  488. case 3:
  489. _b.sent();
  490. return [2 /*return*/, response];
  491. }
  492. });
  493. });
  494. };
  495. return CachingClient;
  496. }());
  497. /**
  498. * @license
  499. * Copyright 2019 Google LLC
  500. *
  501. * Licensed under the Apache License, Version 2.0 (the "License");
  502. * you may not use this file except in compliance with the License.
  503. * You may obtain a copy of the License at
  504. *
  505. * http://www.apache.org/licenses/LICENSE-2.0
  506. *
  507. * Unless required by applicable law or agreed to in writing, software
  508. * distributed under the License is distributed on an "AS IS" BASIS,
  509. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  510. * See the License for the specific language governing permissions and
  511. * limitations under the License.
  512. */
  513. /**
  514. * Attempts to get the most accurate browser language setting.
  515. *
  516. * <p>Adapted from getUserLanguage in packages/auth/src/utils.js for TypeScript.
  517. *
  518. * <p>Defers default language specification to server logic for consistency.
  519. *
  520. * @param navigatorLanguage Enables tests to override read-only {@link NavigatorLanguage}.
  521. */
  522. function getUserLanguage(navigatorLanguage) {
  523. if (navigatorLanguage === void 0) { navigatorLanguage = navigator; }
  524. return (
  525. // Most reliable, but only supported in Chrome/Firefox.
  526. (navigatorLanguage.languages && navigatorLanguage.languages[0]) ||
  527. // Supported in most browsers, but returns the language of the browser
  528. // UI, not the language set in browser settings.
  529. navigatorLanguage.language
  530. // Polyfill otherwise.
  531. );
  532. }
  533. /**
  534. * @license
  535. * Copyright 2019 Google LLC
  536. *
  537. * Licensed under the Apache License, Version 2.0 (the "License");
  538. * you may not use this file except in compliance with the License.
  539. * You may obtain a copy of the License at
  540. *
  541. * http://www.apache.org/licenses/LICENSE-2.0
  542. *
  543. * Unless required by applicable law or agreed to in writing, software
  544. * distributed under the License is distributed on an "AS IS" BASIS,
  545. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  546. * See the License for the specific language governing permissions and
  547. * limitations under the License.
  548. */
  549. /**
  550. * Implements the Client abstraction for the Remote Config REST API.
  551. */
  552. var RestClient = /** @class */ (function () {
  553. function RestClient(firebaseInstallations, sdkVersion, namespace, projectId, apiKey, appId) {
  554. this.firebaseInstallations = firebaseInstallations;
  555. this.sdkVersion = sdkVersion;
  556. this.namespace = namespace;
  557. this.projectId = projectId;
  558. this.apiKey = apiKey;
  559. this.appId = appId;
  560. }
  561. /**
  562. * Fetches from the Remote Config REST API.
  563. *
  564. * @throws a {@link ErrorCode.FETCH_NETWORK} error if {@link GlobalFetch#fetch} can't
  565. * connect to the network.
  566. * @throws a {@link ErrorCode.FETCH_PARSE} error if {@link Response#json} can't parse the
  567. * fetch response.
  568. * @throws a {@link ErrorCode.FETCH_STATUS} error if the service returns an HTTP error status.
  569. */
  570. RestClient.prototype.fetch = function (request) {
  571. return __awaiter(this, void 0, void 0, function () {
  572. var _a, installationId, installationToken, urlBase, url, headers, requestBody, options, fetchPromise, timeoutPromise, response, originalError_1, errorCode, status, responseEtag, config, state, responseBody, originalError_2;
  573. return __generator(this, function (_b) {
  574. switch (_b.label) {
  575. case 0: return [4 /*yield*/, Promise.all([
  576. this.firebaseInstallations.getId(),
  577. this.firebaseInstallations.getToken()
  578. ])];
  579. case 1:
  580. _a = _b.sent(), installationId = _a[0], installationToken = _a[1];
  581. urlBase = window.FIREBASE_REMOTE_CONFIG_URL_BASE ||
  582. 'https://firebaseremoteconfig.googleapis.com';
  583. url = "".concat(urlBase, "/v1/projects/").concat(this.projectId, "/namespaces/").concat(this.namespace, ":fetch?key=").concat(this.apiKey);
  584. headers = {
  585. 'Content-Type': 'application/json',
  586. 'Content-Encoding': 'gzip',
  587. // Deviates from pure decorator by not passing max-age header since we don't currently have
  588. // service behavior using that header.
  589. 'If-None-Match': request.eTag || '*'
  590. };
  591. requestBody = {
  592. /* eslint-disable camelcase */
  593. sdk_version: this.sdkVersion,
  594. app_instance_id: installationId,
  595. app_instance_id_token: installationToken,
  596. app_id: this.appId,
  597. language_code: getUserLanguage()
  598. /* eslint-enable camelcase */
  599. };
  600. options = {
  601. method: 'POST',
  602. headers: headers,
  603. body: JSON.stringify(requestBody)
  604. };
  605. fetchPromise = fetch(url, options);
  606. timeoutPromise = new Promise(function (_resolve, reject) {
  607. // Maps async event listener to Promise API.
  608. request.signal.addEventListener(function () {
  609. // Emulates https://heycam.github.io/webidl/#aborterror
  610. var error = new Error('The operation was aborted.');
  611. error.name = 'AbortError';
  612. reject(error);
  613. });
  614. });
  615. _b.label = 2;
  616. case 2:
  617. _b.trys.push([2, 5, , 6]);
  618. return [4 /*yield*/, Promise.race([fetchPromise, timeoutPromise])];
  619. case 3:
  620. _b.sent();
  621. return [4 /*yield*/, fetchPromise];
  622. case 4:
  623. response = _b.sent();
  624. return [3 /*break*/, 6];
  625. case 5:
  626. originalError_1 = _b.sent();
  627. errorCode = "fetch-client-network" /* ErrorCode.FETCH_NETWORK */;
  628. if ((originalError_1 === null || originalError_1 === void 0 ? void 0 : originalError_1.name) === 'AbortError') {
  629. errorCode = "fetch-timeout" /* ErrorCode.FETCH_TIMEOUT */;
  630. }
  631. throw ERROR_FACTORY.create(errorCode, {
  632. originalErrorMessage: originalError_1 === null || originalError_1 === void 0 ? void 0 : originalError_1.message
  633. });
  634. case 6:
  635. status = response.status;
  636. responseEtag = response.headers.get('ETag') || undefined;
  637. if (!(response.status === 200)) return [3 /*break*/, 11];
  638. responseBody = void 0;
  639. _b.label = 7;
  640. case 7:
  641. _b.trys.push([7, 9, , 10]);
  642. return [4 /*yield*/, response.json()];
  643. case 8:
  644. responseBody = _b.sent();
  645. return [3 /*break*/, 10];
  646. case 9:
  647. originalError_2 = _b.sent();
  648. throw ERROR_FACTORY.create("fetch-client-parse" /* ErrorCode.FETCH_PARSE */, {
  649. originalErrorMessage: originalError_2 === null || originalError_2 === void 0 ? void 0 : originalError_2.message
  650. });
  651. case 10:
  652. config = responseBody['entries'];
  653. state = responseBody['state'];
  654. _b.label = 11;
  655. case 11:
  656. // Normalizes based on legacy state.
  657. if (state === 'INSTANCE_STATE_UNSPECIFIED') {
  658. status = 500;
  659. }
  660. else if (state === 'NO_CHANGE') {
  661. status = 304;
  662. }
  663. else if (state === 'NO_TEMPLATE' || state === 'EMPTY_CONFIG') {
  664. // These cases can be fixed remotely, so normalize to safe value.
  665. config = {};
  666. }
  667. // Normalize to exception-based control flow for non-success cases.
  668. // Encapsulates HTTP specifics in this class as much as possible. Status is still the best for
  669. // differentiating success states (200 from 304; the state body param is undefined in a
  670. // standard 304).
  671. if (status !== 304 && status !== 200) {
  672. throw ERROR_FACTORY.create("fetch-status" /* ErrorCode.FETCH_STATUS */, {
  673. httpStatus: status
  674. });
  675. }
  676. return [2 /*return*/, { status: status, eTag: responseEtag, config: config }];
  677. }
  678. });
  679. });
  680. };
  681. return RestClient;
  682. }());
  683. /**
  684. * @license
  685. * Copyright 2019 Google LLC
  686. *
  687. * Licensed under the Apache License, Version 2.0 (the "License");
  688. * you may not use this file except in compliance with the License.
  689. * You may obtain a copy of the License at
  690. *
  691. * http://www.apache.org/licenses/LICENSE-2.0
  692. *
  693. * Unless required by applicable law or agreed to in writing, software
  694. * distributed under the License is distributed on an "AS IS" BASIS,
  695. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  696. * See the License for the specific language governing permissions and
  697. * limitations under the License.
  698. */
  699. /**
  700. * Supports waiting on a backoff by:
  701. *
  702. * <ul>
  703. * <li>Promisifying setTimeout, so we can set a timeout in our Promise chain</li>
  704. * <li>Listening on a signal bus for abort events, just like the Fetch API</li>
  705. * <li>Failing in the same way the Fetch API fails, so timing out a live request and a throttled
  706. * request appear the same.</li>
  707. * </ul>
  708. *
  709. * <p>Visible for testing.
  710. */
  711. function setAbortableTimeout(signal, throttleEndTimeMillis) {
  712. return new Promise(function (resolve, reject) {
  713. // Derives backoff from given end time, normalizing negative numbers to zero.
  714. var backoffMillis = Math.max(throttleEndTimeMillis - Date.now(), 0);
  715. var timeout = setTimeout(resolve, backoffMillis);
  716. // Adds listener, rather than sets onabort, because signal is a shared object.
  717. signal.addEventListener(function () {
  718. clearTimeout(timeout);
  719. // If the request completes before this timeout, the rejection has no effect.
  720. reject(ERROR_FACTORY.create("fetch-throttle" /* ErrorCode.FETCH_THROTTLE */, {
  721. throttleEndTimeMillis: throttleEndTimeMillis
  722. }));
  723. });
  724. });
  725. }
  726. /**
  727. * Returns true if the {@link Error} indicates a fetch request may succeed later.
  728. */
  729. function isRetriableError(e) {
  730. if (!(e instanceof FirebaseError) || !e.customData) {
  731. return false;
  732. }
  733. // Uses string index defined by ErrorData, which FirebaseError implements.
  734. var httpStatus = Number(e.customData['httpStatus']);
  735. return (httpStatus === 429 ||
  736. httpStatus === 500 ||
  737. httpStatus === 503 ||
  738. httpStatus === 504);
  739. }
  740. /**
  741. * Decorates a Client with retry logic.
  742. *
  743. * <p>Comparable to CachingClient, but uses backoff logic instead of cache max age and doesn't cache
  744. * responses (because the SDK has no use for error responses).
  745. */
  746. var RetryingClient = /** @class */ (function () {
  747. function RetryingClient(client, storage) {
  748. this.client = client;
  749. this.storage = storage;
  750. }
  751. RetryingClient.prototype.fetch = function (request) {
  752. return __awaiter(this, void 0, void 0, function () {
  753. var throttleMetadata;
  754. return __generator(this, function (_a) {
  755. switch (_a.label) {
  756. case 0: return [4 /*yield*/, this.storage.getThrottleMetadata()];
  757. case 1:
  758. throttleMetadata = (_a.sent()) || {
  759. backoffCount: 0,
  760. throttleEndTimeMillis: Date.now()
  761. };
  762. return [2 /*return*/, this.attemptFetch(request, throttleMetadata)];
  763. }
  764. });
  765. });
  766. };
  767. /**
  768. * A recursive helper for attempting a fetch request repeatedly.
  769. *
  770. * @throws any non-retriable errors.
  771. */
  772. RetryingClient.prototype.attemptFetch = function (request, _a) {
  773. var throttleEndTimeMillis = _a.throttleEndTimeMillis, backoffCount = _a.backoffCount;
  774. return __awaiter(this, void 0, void 0, function () {
  775. var response, e_1, throttleMetadata;
  776. return __generator(this, function (_b) {
  777. switch (_b.label) {
  778. case 0:
  779. // Starts with a (potentially zero) timeout to support resumption from stored state.
  780. // Ensures the throttle end time is honored if the last attempt timed out.
  781. // Note the SDK will never make a request if the fetch timeout expires at this point.
  782. return [4 /*yield*/, setAbortableTimeout(request.signal, throttleEndTimeMillis)];
  783. case 1:
  784. // Starts with a (potentially zero) timeout to support resumption from stored state.
  785. // Ensures the throttle end time is honored if the last attempt timed out.
  786. // Note the SDK will never make a request if the fetch timeout expires at this point.
  787. _b.sent();
  788. _b.label = 2;
  789. case 2:
  790. _b.trys.push([2, 5, , 7]);
  791. return [4 /*yield*/, this.client.fetch(request)];
  792. case 3:
  793. response = _b.sent();
  794. // Note the SDK only clears throttle state if response is success or non-retriable.
  795. return [4 /*yield*/, this.storage.deleteThrottleMetadata()];
  796. case 4:
  797. // Note the SDK only clears throttle state if response is success or non-retriable.
  798. _b.sent();
  799. return [2 /*return*/, response];
  800. case 5:
  801. e_1 = _b.sent();
  802. if (!isRetriableError(e_1)) {
  803. throw e_1;
  804. }
  805. throttleMetadata = {
  806. throttleEndTimeMillis: Date.now() + calculateBackoffMillis(backoffCount),
  807. backoffCount: backoffCount + 1
  808. };
  809. // Persists state.
  810. return [4 /*yield*/, this.storage.setThrottleMetadata(throttleMetadata)];
  811. case 6:
  812. // Persists state.
  813. _b.sent();
  814. return [2 /*return*/, this.attemptFetch(request, throttleMetadata)];
  815. case 7: return [2 /*return*/];
  816. }
  817. });
  818. });
  819. };
  820. return RetryingClient;
  821. }());
  822. /**
  823. * @license
  824. * Copyright 2019 Google LLC
  825. *
  826. * Licensed under the Apache License, Version 2.0 (the "License");
  827. * you may not use this file except in compliance with the License.
  828. * You may obtain a copy of the License at
  829. *
  830. * http://www.apache.org/licenses/LICENSE-2.0
  831. *
  832. * Unless required by applicable law or agreed to in writing, software
  833. * distributed under the License is distributed on an "AS IS" BASIS,
  834. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  835. * See the License for the specific language governing permissions and
  836. * limitations under the License.
  837. */
  838. var DEFAULT_FETCH_TIMEOUT_MILLIS = 60 * 1000; // One minute
  839. var DEFAULT_CACHE_MAX_AGE_MILLIS = 12 * 60 * 60 * 1000; // Twelve hours.
  840. /**
  841. * Encapsulates business logic mapping network and storage dependencies to the public SDK API.
  842. *
  843. * See {@link https://github.com/FirebasePrivate/firebase-js-sdk/blob/master/packages/firebase/index.d.ts|interface documentation} for method descriptions.
  844. */
  845. var RemoteConfig = /** @class */ (function () {
  846. function RemoteConfig(
  847. // Required by FirebaseServiceFactory interface.
  848. app,
  849. // JS doesn't support private yet
  850. // (https://github.com/tc39/proposal-class-fields#private-fields), so we hint using an
  851. // underscore prefix.
  852. /**
  853. * @internal
  854. */
  855. _client,
  856. /**
  857. * @internal
  858. */
  859. _storageCache,
  860. /**
  861. * @internal
  862. */
  863. _storage,
  864. /**
  865. * @internal
  866. */
  867. _logger) {
  868. this.app = app;
  869. this._client = _client;
  870. this._storageCache = _storageCache;
  871. this._storage = _storage;
  872. this._logger = _logger;
  873. /**
  874. * Tracks completion of initialization promise.
  875. * @internal
  876. */
  877. this._isInitializationComplete = false;
  878. this.settings = {
  879. fetchTimeoutMillis: DEFAULT_FETCH_TIMEOUT_MILLIS,
  880. minimumFetchIntervalMillis: DEFAULT_CACHE_MAX_AGE_MILLIS
  881. };
  882. this.defaultConfig = {};
  883. }
  884. Object.defineProperty(RemoteConfig.prototype, "fetchTimeMillis", {
  885. get: function () {
  886. return this._storageCache.getLastSuccessfulFetchTimestampMillis() || -1;
  887. },
  888. enumerable: false,
  889. configurable: true
  890. });
  891. Object.defineProperty(RemoteConfig.prototype, "lastFetchStatus", {
  892. get: function () {
  893. return this._storageCache.getLastFetchStatus() || 'no-fetch-yet';
  894. },
  895. enumerable: false,
  896. configurable: true
  897. });
  898. return RemoteConfig;
  899. }());
  900. /**
  901. * @license
  902. * Copyright 2019 Google LLC
  903. *
  904. * Licensed under the Apache License, Version 2.0 (the "License");
  905. * you may not use this file except in compliance with the License.
  906. * You may obtain a copy of the License at
  907. *
  908. * http://www.apache.org/licenses/LICENSE-2.0
  909. *
  910. * Unless required by applicable law or agreed to in writing, software
  911. * distributed under the License is distributed on an "AS IS" BASIS,
  912. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  913. * See the License for the specific language governing permissions and
  914. * limitations under the License.
  915. */
  916. /**
  917. * Converts an error event associated with a {@link IDBRequest} to a {@link FirebaseError}.
  918. */
  919. function toFirebaseError(event, errorCode) {
  920. var originalError = event.target.error || undefined;
  921. return ERROR_FACTORY.create(errorCode, {
  922. originalErrorMessage: originalError && (originalError === null || originalError === void 0 ? void 0 : originalError.message)
  923. });
  924. }
  925. /**
  926. * A general-purpose store keyed by app + namespace + {@link
  927. * ProjectNamespaceKeyFieldValue}.
  928. *
  929. * <p>The Remote Config SDK can be used with multiple app installations, and each app can interact
  930. * with multiple namespaces, so this store uses app (ID + name) and namespace as common parent keys
  931. * for a set of key-value pairs. See {@link Storage#createCompositeKey}.
  932. *
  933. * <p>Visible for testing.
  934. */
  935. var APP_NAMESPACE_STORE = 'app_namespace_store';
  936. var DB_NAME = 'firebase_remote_config';
  937. var DB_VERSION = 1;
  938. // Visible for testing.
  939. function openDatabase() {
  940. return new Promise(function (resolve, reject) {
  941. try {
  942. var request = indexedDB.open(DB_NAME, DB_VERSION);
  943. request.onerror = function (event) {
  944. reject(toFirebaseError(event, "storage-open" /* ErrorCode.STORAGE_OPEN */));
  945. };
  946. request.onsuccess = function (event) {
  947. resolve(event.target.result);
  948. };
  949. request.onupgradeneeded = function (event) {
  950. var db = event.target.result;
  951. // We don't use 'break' in this switch statement, the fall-through
  952. // behavior is what we want, because if there are multiple versions between
  953. // the old version and the current version, we want ALL the migrations
  954. // that correspond to those versions to run, not only the last one.
  955. // eslint-disable-next-line default-case
  956. switch (event.oldVersion) {
  957. case 0:
  958. db.createObjectStore(APP_NAMESPACE_STORE, {
  959. keyPath: 'compositeKey'
  960. });
  961. }
  962. };
  963. }
  964. catch (error) {
  965. reject(ERROR_FACTORY.create("storage-open" /* ErrorCode.STORAGE_OPEN */, {
  966. originalErrorMessage: error === null || error === void 0 ? void 0 : error.message
  967. }));
  968. }
  969. });
  970. }
  971. /**
  972. * Abstracts data persistence.
  973. */
  974. var Storage = /** @class */ (function () {
  975. /**
  976. * @param appId enables storage segmentation by app (ID + name).
  977. * @param appName enables storage segmentation by app (ID + name).
  978. * @param namespace enables storage segmentation by namespace.
  979. */
  980. function Storage(appId, appName, namespace, openDbPromise) {
  981. if (openDbPromise === void 0) { openDbPromise = openDatabase(); }
  982. this.appId = appId;
  983. this.appName = appName;
  984. this.namespace = namespace;
  985. this.openDbPromise = openDbPromise;
  986. }
  987. Storage.prototype.getLastFetchStatus = function () {
  988. return this.get('last_fetch_status');
  989. };
  990. Storage.prototype.setLastFetchStatus = function (status) {
  991. return this.set('last_fetch_status', status);
  992. };
  993. // This is comparable to a cache entry timestamp. If we need to expire other data, we could
  994. // consider adding timestamp to all storage records and an optional max age arg to getters.
  995. Storage.prototype.getLastSuccessfulFetchTimestampMillis = function () {
  996. return this.get('last_successful_fetch_timestamp_millis');
  997. };
  998. Storage.prototype.setLastSuccessfulFetchTimestampMillis = function (timestamp) {
  999. return this.set('last_successful_fetch_timestamp_millis', timestamp);
  1000. };
  1001. Storage.prototype.getLastSuccessfulFetchResponse = function () {
  1002. return this.get('last_successful_fetch_response');
  1003. };
  1004. Storage.prototype.setLastSuccessfulFetchResponse = function (response) {
  1005. return this.set('last_successful_fetch_response', response);
  1006. };
  1007. Storage.prototype.getActiveConfig = function () {
  1008. return this.get('active_config');
  1009. };
  1010. Storage.prototype.setActiveConfig = function (config) {
  1011. return this.set('active_config', config);
  1012. };
  1013. Storage.prototype.getActiveConfigEtag = function () {
  1014. return this.get('active_config_etag');
  1015. };
  1016. Storage.prototype.setActiveConfigEtag = function (etag) {
  1017. return this.set('active_config_etag', etag);
  1018. };
  1019. Storage.prototype.getThrottleMetadata = function () {
  1020. return this.get('throttle_metadata');
  1021. };
  1022. Storage.prototype.setThrottleMetadata = function (metadata) {
  1023. return this.set('throttle_metadata', metadata);
  1024. };
  1025. Storage.prototype.deleteThrottleMetadata = function () {
  1026. return this.delete('throttle_metadata');
  1027. };
  1028. Storage.prototype.get = function (key) {
  1029. return __awaiter(this, void 0, void 0, function () {
  1030. var db;
  1031. var _this = this;
  1032. return __generator(this, function (_a) {
  1033. switch (_a.label) {
  1034. case 0: return [4 /*yield*/, this.openDbPromise];
  1035. case 1:
  1036. db = _a.sent();
  1037. return [2 /*return*/, new Promise(function (resolve, reject) {
  1038. var transaction = db.transaction([APP_NAMESPACE_STORE], 'readonly');
  1039. var objectStore = transaction.objectStore(APP_NAMESPACE_STORE);
  1040. var compositeKey = _this.createCompositeKey(key);
  1041. try {
  1042. var request = objectStore.get(compositeKey);
  1043. request.onerror = function (event) {
  1044. reject(toFirebaseError(event, "storage-get" /* ErrorCode.STORAGE_GET */));
  1045. };
  1046. request.onsuccess = function (event) {
  1047. var result = event.target.result;
  1048. if (result) {
  1049. resolve(result.value);
  1050. }
  1051. else {
  1052. resolve(undefined);
  1053. }
  1054. };
  1055. }
  1056. catch (e) {
  1057. reject(ERROR_FACTORY.create("storage-get" /* ErrorCode.STORAGE_GET */, {
  1058. originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
  1059. }));
  1060. }
  1061. })];
  1062. }
  1063. });
  1064. });
  1065. };
  1066. Storage.prototype.set = function (key, value) {
  1067. return __awaiter(this, void 0, void 0, function () {
  1068. var db;
  1069. var _this = this;
  1070. return __generator(this, function (_a) {
  1071. switch (_a.label) {
  1072. case 0: return [4 /*yield*/, this.openDbPromise];
  1073. case 1:
  1074. db = _a.sent();
  1075. return [2 /*return*/, new Promise(function (resolve, reject) {
  1076. var transaction = db.transaction([APP_NAMESPACE_STORE], 'readwrite');
  1077. var objectStore = transaction.objectStore(APP_NAMESPACE_STORE);
  1078. var compositeKey = _this.createCompositeKey(key);
  1079. try {
  1080. var request = objectStore.put({
  1081. compositeKey: compositeKey,
  1082. value: value
  1083. });
  1084. request.onerror = function (event) {
  1085. reject(toFirebaseError(event, "storage-set" /* ErrorCode.STORAGE_SET */));
  1086. };
  1087. request.onsuccess = function () {
  1088. resolve();
  1089. };
  1090. }
  1091. catch (e) {
  1092. reject(ERROR_FACTORY.create("storage-set" /* ErrorCode.STORAGE_SET */, {
  1093. originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
  1094. }));
  1095. }
  1096. })];
  1097. }
  1098. });
  1099. });
  1100. };
  1101. Storage.prototype.delete = function (key) {
  1102. return __awaiter(this, void 0, void 0, function () {
  1103. var db;
  1104. var _this = this;
  1105. return __generator(this, function (_a) {
  1106. switch (_a.label) {
  1107. case 0: return [4 /*yield*/, this.openDbPromise];
  1108. case 1:
  1109. db = _a.sent();
  1110. return [2 /*return*/, new Promise(function (resolve, reject) {
  1111. var transaction = db.transaction([APP_NAMESPACE_STORE], 'readwrite');
  1112. var objectStore = transaction.objectStore(APP_NAMESPACE_STORE);
  1113. var compositeKey = _this.createCompositeKey(key);
  1114. try {
  1115. var request = objectStore.delete(compositeKey);
  1116. request.onerror = function (event) {
  1117. reject(toFirebaseError(event, "storage-delete" /* ErrorCode.STORAGE_DELETE */));
  1118. };
  1119. request.onsuccess = function () {
  1120. resolve();
  1121. };
  1122. }
  1123. catch (e) {
  1124. reject(ERROR_FACTORY.create("storage-delete" /* ErrorCode.STORAGE_DELETE */, {
  1125. originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
  1126. }));
  1127. }
  1128. })];
  1129. }
  1130. });
  1131. });
  1132. };
  1133. // Facilitates composite key functionality (which is unsupported in IE).
  1134. Storage.prototype.createCompositeKey = function (key) {
  1135. return [this.appId, this.appName, this.namespace, key].join();
  1136. };
  1137. return Storage;
  1138. }());
  1139. /**
  1140. * @license
  1141. * Copyright 2019 Google LLC
  1142. *
  1143. * Licensed under the Apache License, Version 2.0 (the "License");
  1144. * you may not use this file except in compliance with the License.
  1145. * You may obtain a copy of the License at
  1146. *
  1147. * http://www.apache.org/licenses/LICENSE-2.0
  1148. *
  1149. * Unless required by applicable law or agreed to in writing, software
  1150. * distributed under the License is distributed on an "AS IS" BASIS,
  1151. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1152. * See the License for the specific language governing permissions and
  1153. * limitations under the License.
  1154. */
  1155. /**
  1156. * A memory cache layer over storage to support the SDK's synchronous read requirements.
  1157. */
  1158. var StorageCache = /** @class */ (function () {
  1159. function StorageCache(storage) {
  1160. this.storage = storage;
  1161. }
  1162. /**
  1163. * Memory-only getters
  1164. */
  1165. StorageCache.prototype.getLastFetchStatus = function () {
  1166. return this.lastFetchStatus;
  1167. };
  1168. StorageCache.prototype.getLastSuccessfulFetchTimestampMillis = function () {
  1169. return this.lastSuccessfulFetchTimestampMillis;
  1170. };
  1171. StorageCache.prototype.getActiveConfig = function () {
  1172. return this.activeConfig;
  1173. };
  1174. /**
  1175. * Read-ahead getter
  1176. */
  1177. StorageCache.prototype.loadFromStorage = function () {
  1178. return __awaiter(this, void 0, void 0, function () {
  1179. var lastFetchStatusPromise, lastSuccessfulFetchTimestampMillisPromise, activeConfigPromise, lastFetchStatus, lastSuccessfulFetchTimestampMillis, activeConfig;
  1180. return __generator(this, function (_a) {
  1181. switch (_a.label) {
  1182. case 0:
  1183. lastFetchStatusPromise = this.storage.getLastFetchStatus();
  1184. lastSuccessfulFetchTimestampMillisPromise = this.storage.getLastSuccessfulFetchTimestampMillis();
  1185. activeConfigPromise = this.storage.getActiveConfig();
  1186. return [4 /*yield*/, lastFetchStatusPromise];
  1187. case 1:
  1188. lastFetchStatus = _a.sent();
  1189. if (lastFetchStatus) {
  1190. this.lastFetchStatus = lastFetchStatus;
  1191. }
  1192. return [4 /*yield*/, lastSuccessfulFetchTimestampMillisPromise];
  1193. case 2:
  1194. lastSuccessfulFetchTimestampMillis = _a.sent();
  1195. if (lastSuccessfulFetchTimestampMillis) {
  1196. this.lastSuccessfulFetchTimestampMillis =
  1197. lastSuccessfulFetchTimestampMillis;
  1198. }
  1199. return [4 /*yield*/, activeConfigPromise];
  1200. case 3:
  1201. activeConfig = _a.sent();
  1202. if (activeConfig) {
  1203. this.activeConfig = activeConfig;
  1204. }
  1205. return [2 /*return*/];
  1206. }
  1207. });
  1208. });
  1209. };
  1210. /**
  1211. * Write-through setters
  1212. */
  1213. StorageCache.prototype.setLastFetchStatus = function (status) {
  1214. this.lastFetchStatus = status;
  1215. return this.storage.setLastFetchStatus(status);
  1216. };
  1217. StorageCache.prototype.setLastSuccessfulFetchTimestampMillis = function (timestampMillis) {
  1218. this.lastSuccessfulFetchTimestampMillis = timestampMillis;
  1219. return this.storage.setLastSuccessfulFetchTimestampMillis(timestampMillis);
  1220. };
  1221. StorageCache.prototype.setActiveConfig = function (activeConfig) {
  1222. this.activeConfig = activeConfig;
  1223. return this.storage.setActiveConfig(activeConfig);
  1224. };
  1225. return StorageCache;
  1226. }());
  1227. /**
  1228. * @license
  1229. * Copyright 2020 Google LLC
  1230. *
  1231. * Licensed under the Apache License, Version 2.0 (the "License");
  1232. * you may not use this file except in compliance with the License.
  1233. * You may obtain a copy of the License at
  1234. *
  1235. * http://www.apache.org/licenses/LICENSE-2.0
  1236. *
  1237. * Unless required by applicable law or agreed to in writing, software
  1238. * distributed under the License is distributed on an "AS IS" BASIS,
  1239. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1240. * See the License for the specific language governing permissions and
  1241. * limitations under the License.
  1242. */
  1243. function registerRemoteConfig() {
  1244. _registerComponent(new Component(RC_COMPONENT_NAME, remoteConfigFactory, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
  1245. registerVersion(name, version);
  1246. // BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation
  1247. registerVersion(name, version, 'esm5');
  1248. function remoteConfigFactory(container, _a) {
  1249. var namespace = _a.instanceIdentifier;
  1250. /* Dependencies */
  1251. // getImmediate for FirebaseApp will always succeed
  1252. var app = container.getProvider('app').getImmediate();
  1253. // The following call will always succeed because rc has `import '@firebase/installations'`
  1254. var installations = container
  1255. .getProvider('installations-internal')
  1256. .getImmediate();
  1257. // Guards against the SDK being used in non-browser environments.
  1258. if (typeof window === 'undefined') {
  1259. throw ERROR_FACTORY.create("registration-window" /* ErrorCode.REGISTRATION_WINDOW */);
  1260. }
  1261. // Guards against the SDK being used when indexedDB is not available.
  1262. if (!isIndexedDBAvailable()) {
  1263. throw ERROR_FACTORY.create("indexed-db-unavailable" /* ErrorCode.INDEXED_DB_UNAVAILABLE */);
  1264. }
  1265. // Normalizes optional inputs.
  1266. var _b = app.options, projectId = _b.projectId, apiKey = _b.apiKey, appId = _b.appId;
  1267. if (!projectId) {
  1268. throw ERROR_FACTORY.create("registration-project-id" /* ErrorCode.REGISTRATION_PROJECT_ID */);
  1269. }
  1270. if (!apiKey) {
  1271. throw ERROR_FACTORY.create("registration-api-key" /* ErrorCode.REGISTRATION_API_KEY */);
  1272. }
  1273. if (!appId) {
  1274. throw ERROR_FACTORY.create("registration-app-id" /* ErrorCode.REGISTRATION_APP_ID */);
  1275. }
  1276. namespace = namespace || 'firebase';
  1277. var storage = new Storage(appId, app.name, namespace);
  1278. var storageCache = new StorageCache(storage);
  1279. var logger = new Logger(name);
  1280. // Sets ERROR as the default log level.
  1281. // See RemoteConfig#setLogLevel for corresponding normalization to ERROR log level.
  1282. logger.logLevel = LogLevel.ERROR;
  1283. var restClient = new RestClient(installations,
  1284. // Uses the JS SDK version, by which the RC package version can be deduced, if necessary.
  1285. SDK_VERSION, namespace, projectId, apiKey, appId);
  1286. var retryingClient = new RetryingClient(restClient, storage);
  1287. var cachingClient = new CachingClient(retryingClient, storage, storageCache, logger);
  1288. var remoteConfigInstance = new RemoteConfig(app, cachingClient, storageCache, storage, logger);
  1289. // Starts warming cache.
  1290. // eslint-disable-next-line @typescript-eslint/no-floating-promises
  1291. ensureInitialized(remoteConfigInstance);
  1292. return remoteConfigInstance;
  1293. }
  1294. }
  1295. /**
  1296. * @license
  1297. * Copyright 2020 Google LLC
  1298. *
  1299. * Licensed under the Apache License, Version 2.0 (the "License");
  1300. * you may not use this file except in compliance with the License.
  1301. * You may obtain a copy of the License at
  1302. *
  1303. * http://www.apache.org/licenses/LICENSE-2.0
  1304. *
  1305. * Unless required by applicable law or agreed to in writing, software
  1306. * distributed under the License is distributed on an "AS IS" BASIS,
  1307. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1308. * See the License for the specific language governing permissions and
  1309. * limitations under the License.
  1310. */
  1311. // This API is put in a separate file, so we can stub fetchConfig and activate in tests.
  1312. // It's not possible to stub standalone functions from the same module.
  1313. /**
  1314. *
  1315. * Performs fetch and activate operations, as a convenience.
  1316. *
  1317. * @param remoteConfig - The {@link RemoteConfig} instance.
  1318. *
  1319. * @returns A `Promise` which resolves to true if the current call activated the fetched configs.
  1320. * If the fetched configs were already activated, the `Promise` will resolve to false.
  1321. *
  1322. * @public
  1323. */
  1324. function fetchAndActivate(remoteConfig) {
  1325. return __awaiter(this, void 0, void 0, function () {
  1326. return __generator(this, function (_a) {
  1327. switch (_a.label) {
  1328. case 0:
  1329. remoteConfig = getModularInstance(remoteConfig);
  1330. return [4 /*yield*/, fetchConfig(remoteConfig)];
  1331. case 1:
  1332. _a.sent();
  1333. return [2 /*return*/, activate(remoteConfig)];
  1334. }
  1335. });
  1336. });
  1337. }
  1338. /**
  1339. * This method provides two different checks:
  1340. *
  1341. * 1. Check if IndexedDB exists in the browser environment.
  1342. * 2. Check if the current browser context allows IndexedDB `open()` calls.
  1343. *
  1344. * @returns A `Promise` which resolves to true if a {@link RemoteConfig} instance
  1345. * can be initialized in this environment, or false if it cannot.
  1346. * @public
  1347. */
  1348. function isSupported() {
  1349. return __awaiter(this, void 0, void 0, function () {
  1350. var isDBOpenable;
  1351. return __generator(this, function (_a) {
  1352. switch (_a.label) {
  1353. case 0:
  1354. if (!isIndexedDBAvailable()) {
  1355. return [2 /*return*/, false];
  1356. }
  1357. _a.label = 1;
  1358. case 1:
  1359. _a.trys.push([1, 3, , 4]);
  1360. return [4 /*yield*/, validateIndexedDBOpenable()];
  1361. case 2:
  1362. isDBOpenable = _a.sent();
  1363. return [2 /*return*/, isDBOpenable];
  1364. case 3:
  1365. _a.sent();
  1366. return [2 /*return*/, false];
  1367. case 4: return [2 /*return*/];
  1368. }
  1369. });
  1370. });
  1371. }
  1372. /**
  1373. * Firebase Remote Config
  1374. *
  1375. * @packageDocumentation
  1376. */
  1377. /** register component and version */
  1378. registerRemoteConfig();
  1379. export { activate, ensureInitialized, fetchAndActivate, fetchConfig, getAll, getBoolean, getNumber, getRemoteConfig, getString, getValue, isSupported, setLogLevel };
  1380. //# sourceMappingURL=index.esm.js.map