text: import firebase from '@firebase/app';
  import { Component } from '@firebase/component';
  import { __awaiter, __generator, __spread, __values, __assign } from 'tslib';
  import { ErrorFactory, FirebaseError } from '@firebase/util';
  import { openDb } from 'idb';
  
  var name = "@firebase/installations";
  var version = "0.4.9";
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  var PENDING_TIMEOUT_MS = 10000;
  var PACKAGE_VERSION = "w:" + version;
  var INTERNAL_AUTH_VERSION = 'FIS_v2';
  var INSTALLATIONS_API_URL = 'https://firebaseinstallations.googleapis.com/v1';
  var TOKEN_EXPIRATION_BUFFER = 60 * 60 * 1000; // One hour
  var SERVICE = 'installations';
  var SERVICE_NAME = 'Installations';
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  var _a;
  var ERROR_DESCRIPTION_MAP = (_a = {},
      _a["missing-app-config-values" /* MISSING_APP_CONFIG_VALUES */] = 'Missing App configuration value: "{$valueName}"',
      _a["not-registered" /* NOT_REGISTERED */] = 'Firebase Installation is not registered.',
      _a["installation-not-found" /* INSTALLATION_NOT_FOUND */] = 'Firebase Installation not found.',
      _a["request-failed" /* REQUEST_FAILED */] = '{$requestName} request failed with error "{$serverCode} {$serverStatus}: {$serverMessage}"',
      _a["app-offline" /* APP_OFFLINE */] = 'Could not process request. Application offline.',
      _a["delete-pending-registration" /* DELETE_PENDING_REGISTRATION */] = "Can't delete installation while there is a pending registration request.",
      _a);
  var ERROR_FACTORY = new ErrorFactory(SERVICE, SERVICE_NAME, ERROR_DESCRIPTION_MAP);
  /** Returns true if error is a FirebaseError that is based on an error from the server. */
  function isServerError(error) {
      return (error instanceof FirebaseError &&
          error.code.includes("request-failed" /* REQUEST_FAILED */));
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  function getInstallationsEndpoint(_a) {
      var projectId = _a.projectId;
      return INSTALLATIONS_API_URL + "/projects/" + projectId + "/installations";
  }
  function extractAuthTokenInfoFromResponse(response) {
      return {
          token: response.token,
          requestStatus: 2 /* COMPLETED */,
          expiresIn: getExpiresInFromResponseExpiresIn(response.expiresIn),
          creationTime: Date.now()
      };
  }
  function getErrorFromResponse(requestName, response) {
      return __awaiter(this, void 0, void 0, function () {
          var responseJson, errorData;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0: return [4 /*yield*/, response.json()];
                  case 1:
                      responseJson = _a.sent();
                      errorData = responseJson.error;
                      return [2 /*return*/, ERROR_FACTORY.create("request-failed" /* REQUEST_FAILED */, {
                              requestName: requestName,
                              serverCode: errorData.code,
                              serverMessage: errorData.message,
                              serverStatus: errorData.status
                          })];
              }
          });
      });
  }
  function getHeaders(_a) {
      var apiKey = _a.apiKey;
      return new Headers({
          'Content-Type': 'application/json',
          Accept: 'application/json',
          'x-goog-api-key': apiKey
      });
  }
  function getHeadersWithAuth(appConfig, _a) {
      var refreshToken = _a.refreshToken;
      var headers = getHeaders(appConfig);
      headers.append('Authorization', getAuthorizationHeader(refreshToken));
      return headers;
  }
  /**
   * Calls the passed in fetch wrapper and returns the response.
   * If the returned response has a status of 5xx, re-runs the function once and
   * returns the response.
   */
  function retryIfServerError(fn) {
      return __awaiter(this, void 0, void 0, function () {
          var result;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0: return [4 /*yield*/, fn()];
                  case 1:
                      result = _a.sent();
                      if (result.status >= 500 && result.status < 600) {
                          // Internal Server Error. Retry request.
                          return [2 /*return*/, fn()];
                      }
                      return [2 /*return*/, result];
              }
          });
      });
  }
  function getExpiresInFromResponseExpiresIn(responseExpiresIn) {
      // This works because the server will never respond with fractions of a second.
      return Number(responseExpiresIn.replace('s', '000'));
  }
  function getAuthorizationHeader(refreshToken) {
      return INTERNAL_AUTH_VERSION + " " + refreshToken;
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  function createInstallationRequest(appConfig, _a) {
      var fid = _a.fid;
      return __awaiter(this, void 0, void 0, function () {
          var endpoint, headers, body, request, response, responseValue, registeredInstallationEntry;
          return __generator(this, function (_b) {
              switch (_b.label) {
                  case 0:
                      endpoint = getInstallationsEndpoint(appConfig);
                      headers = getHeaders(appConfig);
                      body = {
                          fid: fid,
                          authVersion: INTERNAL_AUTH_VERSION,
                          appId: appConfig.appId,
                          sdkVersion: PACKAGE_VERSION
                      };
                      request = {
                          method: 'POST',
                          headers: headers,
                          body: JSON.stringify(body)
                      };
                      return [4 /*yield*/, retryIfServerError(function () { return fetch(endpoint, request); })];
                  case 1:
                      response = _b.sent();
                      if (!response.ok) return [3 /*break*/, 3];
                      return [4 /*yield*/, response.json()];
                  case 2:
                      responseValue = _b.sent();
                      registeredInstallationEntry = {
                          fid: responseValue.fid || fid,
                          registrationStatus: 2 /* COMPLETED */,
                          refreshToken: responseValue.refreshToken,
                          authToken: extractAuthTokenInfoFromResponse(responseValue.authToken)
                      };
                      return [2 /*return*/, registeredInstallationEntry];
                  case 3: return [4 /*yield*/, getErrorFromResponse('Create Installation', response)];
                  case 4: throw _b.sent();
              }
          });
      });
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /** Returns a promise that resolves after given time passes. */
  function sleep(ms) {
      return new Promise(function (resolve) {
          setTimeout(resolve, ms);
      });
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  function bufferToBase64UrlSafe(array) {
      var b64 = btoa(String.fromCharCode.apply(String, __spread(array)));
      return b64.replace(/\+/g, '-').replace(/\//g, '_');
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  var VALID_FID_PATTERN = /^[cdef][\w-]{21}$/;
  var INVALID_FID = '';
  /**
   * Generates a new FID using random values from Web Crypto API.
   * Returns an empty string if FID generation fails for any reason.
   */
  function generateFid() {
      try {
          // A valid FID has exactly 22 base64 characters, which is 132 bits, or 16.5
          // bytes. our implementation generates a 17 byte array instead.
          var fidByteArray = new Uint8Array(17);
          var crypto_1 = self.crypto || self.msCrypto;
          crypto_1.getRandomValues(fidByteArray);
          // Replace the first 4 random bits with the constant FID header of 0b0111.
          fidByteArray[0] = 112 + (fidByteArray[0] % 16);
          var fid = encode(fidByteArray);
          return VALID_FID_PATTERN.test(fid) ? fid : INVALID_FID;
      }
      catch (_a) {
          // FID generation errored
          return INVALID_FID;
      }
  }
  /** Converts a FID Uint8Array to a base64 string representation. */
  function encode(fidByteArray) {
      var b64String = bufferToBase64UrlSafe(fidByteArray);
      // Remove the 23rd character that was added because of the extra 4 bits at the
      // end of our 17 byte array, and the '=' padding.
      return b64String.substr(0, 22);
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /** Returns a string key that can be used to identify the app. */
  function getKey(appConfig) {
      return appConfig.appName + "!" + appConfig.appId;
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  var fidChangeCallbacks = new Map();
  /**
   * Calls the onIdChange callbacks with the new FID value, and broadcasts the
   * change to other tabs.
   */
  function fidChanged(appConfig, fid) {
      var key = getKey(appConfig);
      callFidChangeCallbacks(key, fid);
      broadcastFidChange(key, fid);
  }
  function addCallback(appConfig, callback) {
      // Open the broadcast channel if it's not already open,
      // to be able to listen to change events from other tabs.
      getBroadcastChannel();
      var key = getKey(appConfig);
      var callbackSet = fidChangeCallbacks.get(key);
      if (!callbackSet) {
          callbackSet = new Set();
          fidChangeCallbacks.set(key, callbackSet);
      }
      callbackSet.add(callback);
  }
  function removeCallback(appConfig, callback) {
      var key = getKey(appConfig);
      var callbackSet = fidChangeCallbacks.get(key);
      if (!callbackSet) {
          return;
      }
      callbackSet.delete(callback);
      if (callbackSet.size === 0) {
          fidChangeCallbacks.delete(key);
      }
      // Close broadcast channel if there are no more callbacks.
      closeBroadcastChannel();
  }
  function callFidChangeCallbacks(key, fid) {
      var e_1, _a;
      var callbacks = fidChangeCallbacks.get(key);
      if (!callbacks) {
          return;
      }
      try {
          for (var callbacks_1 = __values(callbacks), callbacks_1_1 = callbacks_1.next(); !callbacks_1_1.done; callbacks_1_1 = callbacks_1.next()) {
              var callback = callbacks_1_1.value;
              callback(fid);
          }
      }
      catch (e_1_1) { e_1 = { error: e_1_1 }; }
      finally {
          try {
              if (callbacks_1_1 && !callbacks_1_1.done && (_a = callbacks_1.return)) _a.call(callbacks_1);
          }
          finally { if (e_1) throw e_1.error; }
      }
  }
  function broadcastFidChange(key, fid) {
      var channel = getBroadcastChannel();
      if (channel) {
          channel.postMessage({ key: key, fid: fid });
      }
      closeBroadcastChannel();
  }
  var broadcastChannel = null;
  /** Opens and returns a BroadcastChannel if it is supported by the browser. */
  function getBroadcastChannel() {
      if (!broadcastChannel && 'BroadcastChannel' in self) {
          broadcastChannel = new BroadcastChannel('[Firebase] FID Change');
          broadcastChannel.onmessage = function (e) {
              callFidChangeCallbacks(e.data.key, e.data.fid);
          };
      }
      return broadcastChannel;
  }
  function closeBroadcastChannel() {
      if (fidChangeCallbacks.size === 0 && broadcastChannel) {
          broadcastChannel.close();
          broadcastChannel = null;
      }
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  var DATABASE_NAME = 'firebase-installations-database';
  var DATABASE_VERSION = 1;
  var OBJECT_STORE_NAME = 'firebase-installations-store';
  var dbPromise = null;
  function getDbPromise() {
      if (!dbPromise) {
          dbPromise = openDb(DATABASE_NAME, DATABASE_VERSION, function (upgradeDB) {
              // We don't use 'break' in this switch statement, the fall-through
              // behavior is what we want, because if there are multiple versions between
              // the old version and the current version, we want ALL the migrations
              // that correspond to those versions to run, not only the last one.
              // eslint-disable-next-line default-case
              switch (upgradeDB.oldVersion) {
                  case 0:
                      upgradeDB.createObjectStore(OBJECT_STORE_NAME);
              }
          });
      }
      return dbPromise;
  }
  /** Assigns or overwrites the record for the given key with the given value. */
  function set(appConfig, value) {
      return __awaiter(this, void 0, void 0, function () {
          var key, db, tx, objectStore, oldValue;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      key = getKey(appConfig);
                      return [4 /*yield*/, getDbPromise()];
                  case 1:
                      db = _a.sent();
                      tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
                      objectStore = tx.objectStore(OBJECT_STORE_NAME);
                      return [4 /*yield*/, objectStore.get(key)];
                  case 2:
                      oldValue = _a.sent();
                      return [4 /*yield*/, objectStore.put(value, key)];
                  case 3:
                      _a.sent();
                      return [4 /*yield*/, tx.complete];
                  case 4:
                      _a.sent();
                      if (!oldValue || oldValue.fid !== value.fid) {
                          fidChanged(appConfig, value.fid);
                      }
                      return [2 /*return*/, value];
              }
          });
      });
  }
  /** Removes record(s) from the objectStore that match the given key. */
  function remove(appConfig) {
      return __awaiter(this, void 0, void 0, function () {
          var key, db, tx;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      key = getKey(appConfig);
                      return [4 /*yield*/, getDbPromise()];
                  case 1:
                      db = _a.sent();
                      tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
                      return [4 /*yield*/, tx.objectStore(OBJECT_STORE_NAME).delete(key)];
                  case 2:
                      _a.sent();
                      return [4 /*yield*/, tx.complete];
                  case 3:
                      _a.sent();
                      return [2 /*return*/];
              }
          });
      });
  }
  /**
   * Atomically updates a record with the result of updateFn, which gets
   * called with the current value. If newValue is undefined, the record is
   * deleted instead.
   * @return Updated value
   */
  function update(appConfig, updateFn) {
      return __awaiter(this, void 0, void 0, function () {
          var key, db, tx, store, oldValue, newValue;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      key = getKey(appConfig);
                      return [4 /*yield*/, getDbPromise()];
                  case 1:
                      db = _a.sent();
                      tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
                      store = tx.objectStore(OBJECT_STORE_NAME);
                      return [4 /*yield*/, store.get(key)];
                  case 2:
                      oldValue = _a.sent();
                      newValue = updateFn(oldValue);
                      if (!(newValue === undefined)) return [3 /*break*/, 4];
                      return [4 /*yield*/, store.delete(key)];
                  case 3:
                      _a.sent();
                      return [3 /*break*/, 6];
                  case 4: return [4 /*yield*/, store.put(newValue, key)];
                  case 5:
                      _a.sent();
                      _a.label = 6;
                  case 6: return [4 /*yield*/, tx.complete];
                  case 7:
                      _a.sent();
                      if (newValue && (!oldValue || oldValue.fid !== newValue.fid)) {
                          fidChanged(appConfig, newValue.fid);
                      }
                      return [2 /*return*/, newValue];
              }
          });
      });
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /**
   * Updates and returns the InstallationEntry from the database.
   * Also triggers a registration request if it is necessary and possible.
   */
  function getInstallationEntry(appConfig) {
      return __awaiter(this, void 0, void 0, function () {
          var registrationPromise, installationEntry, _a;
          return __generator(this, function (_b) {
              switch (_b.label) {
                  case 0: return [4 /*yield*/, update(appConfig, function (oldEntry) {
                          var installationEntry = updateOrCreateInstallationEntry(oldEntry);
                          var entryWithPromise = triggerRegistrationIfNecessary(appConfig, installationEntry);
                          registrationPromise = entryWithPromise.registrationPromise;
                          return entryWithPromise.installationEntry;
                      })];
                  case 1:
                      installationEntry = _b.sent();
                      if (!(installationEntry.fid === INVALID_FID)) return [3 /*break*/, 3];
                      _a = {};
                      return [4 /*yield*/, registrationPromise];
                  case 2: 
                  // FID generation failed. Waiting for the FID from the server.
                  return [2 /*return*/, (_a.installationEntry = _b.sent(), _a)];
                  case 3: return [2 /*return*/, {
                          installationEntry: installationEntry,
                          registrationPromise: registrationPromise
                      }];
              }
          });
      });
  }
  /**
   * Creates a new Installation Entry if one does not exist.
   * Also clears timed out pending requests.
   */
  function updateOrCreateInstallationEntry(oldEntry) {
      var entry = oldEntry || {
          fid: generateFid(),
          registrationStatus: 0 /* NOT_STARTED */
      };
      return clearTimedOutRequest(entry);
  }
  /**
   * If the Firebase Installation is not registered yet, this will trigger the
   * registration and return an InProgressInstallationEntry.
   *
   * If registrationPromise does not exist, the installationEntry is guaranteed
   * to be registered.
   */
  function triggerRegistrationIfNecessary(appConfig, installationEntry) {
      if (installationEntry.registrationStatus === 0 /* NOT_STARTED */) {
          if (!navigator.onLine) {
              // Registration required but app is offline.
              var registrationPromiseWithError = Promise.reject(ERROR_FACTORY.create("app-offline" /* APP_OFFLINE */));
              return {
                  installationEntry: installationEntry,
                  registrationPromise: registrationPromiseWithError
              };
          }
          // Try registering. Change status to IN_PROGRESS.
          var inProgressEntry = {
              fid: installationEntry.fid,
              registrationStatus: 1 /* IN_PROGRESS */,
              registrationTime: Date.now()
          };
          var registrationPromise = registerInstallation(appConfig, inProgressEntry);
          return { installationEntry: inProgressEntry, registrationPromise: registrationPromise };
      }
      else if (installationEntry.registrationStatus === 1 /* IN_PROGRESS */) {
          return {
              installationEntry: installationEntry,
              registrationPromise: waitUntilFidRegistration(appConfig)
          };
      }
      else {
          return { installationEntry: installationEntry };
      }
  }
  /** This will be executed only once for each new Firebase Installation. */
  function registerInstallation(appConfig, installationEntry) {
      return __awaiter(this, void 0, void 0, function () {
          var registeredInstallationEntry, e_1;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      _a.trys.push([0, 2, , 7]);
                      return [4 /*yield*/, createInstallationRequest(appConfig, installationEntry)];
                  case 1:
                      registeredInstallationEntry = _a.sent();
                      return [2 /*return*/, set(appConfig, registeredInstallationEntry)];
                  case 2:
                      e_1 = _a.sent();
                      if (!(isServerError(e_1) && e_1.serverCode === 409)) return [3 /*break*/, 4];
                      // Server returned a "FID can not be used" error.
                      // Generate a new ID next time.
                      return [4 /*yield*/, remove(appConfig)];
                  case 3:
                      // Server returned a "FID can not be used" error.
                      // Generate a new ID next time.
                      _a.sent();
                      return [3 /*break*/, 6];
                  case 4: 
                  // Registration failed. Set FID as not registered.
                  return [4 /*yield*/, set(appConfig, {
                          fid: installationEntry.fid,
                          registrationStatus: 0 /* NOT_STARTED */
                      })];
                  case 5:
                      // Registration failed. Set FID as not registered.
                      _a.sent();
                      _a.label = 6;
                  case 6: throw e_1;
                  case 7: return [2 /*return*/];
              }
          });
      });
  }
  /** Call if FID registration is pending in another request. */
  function waitUntilFidRegistration(appConfig) {
      return __awaiter(this, void 0, void 0, function () {
          var entry, _a, installationEntry, registrationPromise;
          return __generator(this, function (_b) {
              switch (_b.label) {
                  case 0: return [4 /*yield*/, updateInstallationRequest(appConfig)];
                  case 1:
                      entry = _b.sent();
                      _b.label = 2;
                  case 2:
                      if (!(entry.registrationStatus === 1 /* IN_PROGRESS */)) return [3 /*break*/, 5];
                      // createInstallation request still in progress.
                      return [4 /*yield*/, sleep(100)];
                  case 3:
                      // createInstallation request still in progress.
                      _b.sent();
                      return [4 /*yield*/, updateInstallationRequest(appConfig)];
                  case 4:
                      entry = _b.sent();
                      return [3 /*break*/, 2];
                  case 5:
                      if (!(entry.registrationStatus === 0 /* NOT_STARTED */)) return [3 /*break*/, 7];
                      return [4 /*yield*/, getInstallationEntry(appConfig)];
                  case 6:
                      _a = _b.sent(), installationEntry = _a.installationEntry, registrationPromise = _a.registrationPromise;
                      if (registrationPromise) {
                          return [2 /*return*/, registrationPromise];
                      }
                      else {
                          // if there is no registrationPromise, entry is registered.
                          return [2 /*return*/, installationEntry];
                      }
                  case 7: return [2 /*return*/, entry];
              }
          });
      });
  }
  /**
   * Called only if there is a CreateInstallation request in progress.
   *
   * Updates the InstallationEntry in the DB based on the status of the
   * CreateInstallation request.
   *
   * Returns the updated InstallationEntry.
   */
  function updateInstallationRequest(appConfig) {
      return update(appConfig, function (oldEntry) {
          if (!oldEntry) {
              throw ERROR_FACTORY.create("installation-not-found" /* INSTALLATION_NOT_FOUND */);
          }
          return clearTimedOutRequest(oldEntry);
      });
  }
  function clearTimedOutRequest(entry) {
      if (hasInstallationRequestTimedOut(entry)) {
          return {
              fid: entry.fid,
              registrationStatus: 0 /* NOT_STARTED */
          };
      }
      return entry;
  }
  function hasInstallationRequestTimedOut(installationEntry) {
      return (installationEntry.registrationStatus === 1 /* IN_PROGRESS */ &&
          installationEntry.registrationTime + PENDING_TIMEOUT_MS < Date.now());
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  function generateAuthTokenRequest(_a, installationEntry) {
      var appConfig = _a.appConfig, platformLoggerProvider = _a.platformLoggerProvider;
      return __awaiter(this, void 0, void 0, function () {
          var endpoint, headers, platformLogger, body, request, response, responseValue, completedAuthToken;
          return __generator(this, function (_b) {
              switch (_b.label) {
                  case 0:
                      endpoint = getGenerateAuthTokenEndpoint(appConfig, installationEntry);
                      headers = getHeadersWithAuth(appConfig, installationEntry);
                      platformLogger = platformLoggerProvider.getImmediate({
                          optional: true
                      });
                      if (platformLogger) {
                          headers.append('x-firebase-client', platformLogger.getPlatformInfoString());
                      }
                      body = {
                          installation: {
                              sdkVersion: PACKAGE_VERSION
                          }
                      };
                      request = {
                          method: 'POST',
                          headers: headers,
                          body: JSON.stringify(body)
                      };
                      return [4 /*yield*/, retryIfServerError(function () { return fetch(endpoint, request); })];
                  case 1:
                      response = _b.sent();
                      if (!response.ok) return [3 /*break*/, 3];
                      return [4 /*yield*/, response.json()];
                  case 2:
                      responseValue = _b.sent();
                      completedAuthToken = extractAuthTokenInfoFromResponse(responseValue);
                      return [2 /*return*/, completedAuthToken];
                  case 3: return [4 /*yield*/, getErrorFromResponse('Generate Auth Token', response)];
                  case 4: throw _b.sent();
              }
          });
      });
  }
  function getGenerateAuthTokenEndpoint(appConfig, _a) {
      var fid = _a.fid;
      return getInstallationsEndpoint(appConfig) + "/" + fid + "/authTokens:generate";
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /**
   * Returns a valid authentication token for the installation. Generates a new
   * token if one doesn't exist, is expired or about to expire.
   *
   * Should only be called if the Firebase Installation is registered.
   */
  function refreshAuthToken(dependencies, forceRefresh) {
      if (forceRefresh === void 0) { forceRefresh = false; }
      return __awaiter(this, void 0, void 0, function () {
          var tokenPromise, entry, authToken, _a;
          return __generator(this, function (_b) {
              switch (_b.label) {
                  case 0: return [4 /*yield*/, update(dependencies.appConfig, function (oldEntry) {
                          if (!isEntryRegistered(oldEntry)) {
                              throw ERROR_FACTORY.create("not-registered" /* NOT_REGISTERED */);
                          }
                          var oldAuthToken = oldEntry.authToken;
                          if (!forceRefresh && isAuthTokenValid(oldAuthToken)) {
                              // There is a valid token in the DB.
                              return oldEntry;
                          }
                          else if (oldAuthToken.requestStatus === 1 /* IN_PROGRESS */) {
                              // There already is a token request in progress.
                              tokenPromise = waitUntilAuthTokenRequest(dependencies, forceRefresh);
                              return oldEntry;
                          }
                          else {
                              // No token or token expired.
                              if (!navigator.onLine) {
                                  throw ERROR_FACTORY.create("app-offline" /* APP_OFFLINE */);
                              }
                              var inProgressEntry = makeAuthTokenRequestInProgressEntry(oldEntry);
                              tokenPromise = fetchAuthTokenFromServer(dependencies, inProgressEntry);
                              return inProgressEntry;
                          }
                      })];
                  case 1:
                      entry = _b.sent();
                      if (!tokenPromise) return [3 /*break*/, 3];
                      return [4 /*yield*/, tokenPromise];
                  case 2:
                      _a = _b.sent();
                      return [3 /*break*/, 4];
                  case 3:
                      _a = entry.authToken;
                      _b.label = 4;
                  case 4:
                      authToken = _a;
                      return [2 /*return*/, authToken];
              }
          });
      });
  }
  /**
   * Call only if FID is registered and Auth Token request is in progress.
   *
   * Waits until the current pending request finishes. If the request times out,
   * tries once in this thread as well.
   */
  function waitUntilAuthTokenRequest(dependencies, forceRefresh) {
      return __awaiter(this, void 0, void 0, function () {
          var entry, authToken;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0: return [4 /*yield*/, updateAuthTokenRequest(dependencies.appConfig)];
                  case 1:
                      entry = _a.sent();
                      _a.label = 2;
                  case 2:
                      if (!(entry.authToken.requestStatus === 1 /* IN_PROGRESS */)) return [3 /*break*/, 5];
                      // generateAuthToken still in progress.
                      return [4 /*yield*/, sleep(100)];
                  case 3:
                      // generateAuthToken still in progress.
                      _a.sent();
                      return [4 /*yield*/, updateAuthTokenRequest(dependencies.appConfig)];
                  case 4:
                      entry = _a.sent();
                      return [3 /*break*/, 2];
                  case 5:
                      authToken = entry.authToken;
                      if (authToken.requestStatus === 0 /* NOT_STARTED */) {
                          // The request timed out or failed in a different call. Try again.
                          return [2 /*return*/, refreshAuthToken(dependencies, forceRefresh)];
                      }
                      else {
                          return [2 /*return*/, authToken];
                      }
              }
          });
      });
  }
  /**
   * Called only if there is a GenerateAuthToken request in progress.
   *
   * Updates the InstallationEntry in the DB based on the status of the
   * GenerateAuthToken request.
   *
   * Returns the updated InstallationEntry.
   */
  function updateAuthTokenRequest(appConfig) {
      return update(appConfig, function (oldEntry) {
          if (!isEntryRegistered(oldEntry)) {
              throw ERROR_FACTORY.create("not-registered" /* NOT_REGISTERED */);
          }
          var oldAuthToken = oldEntry.authToken;
          if (hasAuthTokenRequestTimedOut(oldAuthToken)) {
              return __assign(__assign({}, oldEntry), { authToken: { requestStatus: 0 /* NOT_STARTED */ } });
          }
          return oldEntry;
      });
  }
  function fetchAuthTokenFromServer(dependencies, installationEntry) {
      return __awaiter(this, void 0, void 0, function () {
          var authToken, updatedInstallationEntry, e_1, updatedInstallationEntry;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      _a.trys.push([0, 3, , 8]);
                      return [4 /*yield*/, generateAuthTokenRequest(dependencies, installationEntry)];
                  case 1:
                      authToken = _a.sent();
                      updatedInstallationEntry = __assign(__assign({}, installationEntry), { authToken: authToken });
                      return [4 /*yield*/, set(dependencies.appConfig, updatedInstallationEntry)];
                  case 2:
                      _a.sent();
                      return [2 /*return*/, authToken];
                  case 3:
                      e_1 = _a.sent();
                      if (!(isServerError(e_1) && (e_1.serverCode === 401 || e_1.serverCode === 404))) return [3 /*break*/, 5];
                      // Server returned a "FID not found" or a "Invalid authentication" error.
                      // Generate a new ID next time.
                      return [4 /*yield*/, remove(dependencies.appConfig)];
                  case 4:
                      // Server returned a "FID not found" or a "Invalid authentication" error.
                      // Generate a new ID next time.
                      _a.sent();
                      return [3 /*break*/, 7];
                  case 5:
                      updatedInstallationEntry = __assign(__assign({}, installationEntry), { authToken: { requestStatus: 0 /* NOT_STARTED */ } });
                      return [4 /*yield*/, set(dependencies.appConfig, updatedInstallationEntry)];
                  case 6:
                      _a.sent();
                      _a.label = 7;
                  case 7: throw e_1;
                  case 8: return [2 /*return*/];
              }
          });
      });
  }
  function isEntryRegistered(installationEntry) {
      return (installationEntry !== undefined &&
          installationEntry.registrationStatus === 2 /* COMPLETED */);
  }
  function isAuthTokenValid(authToken) {
      return (authToken.requestStatus === 2 /* COMPLETED */ &&
          !isAuthTokenExpired(authToken));
  }
  function isAuthTokenExpired(authToken) {
      var now = Date.now();
      return (now < authToken.creationTime ||
          authToken.creationTime + authToken.expiresIn < now + TOKEN_EXPIRATION_BUFFER);
  }
  /** Returns an updated InstallationEntry with an InProgressAuthToken. */
  function makeAuthTokenRequestInProgressEntry(oldEntry) {
      var inProgressAuthToken = {
          requestStatus: 1 /* IN_PROGRESS */,
          requestTime: Date.now()
      };
      return __assign(__assign({}, oldEntry), { authToken: inProgressAuthToken });
  }
  function hasAuthTokenRequestTimedOut(authToken) {
      return (authToken.requestStatus === 1 /* IN_PROGRESS */ &&
          authToken.requestTime + PENDING_TIMEOUT_MS < Date.now());
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  function getId(dependencies) {
      return __awaiter(this, void 0, void 0, function () {
          var _a, installationEntry, registrationPromise;
          return __generator(this, function (_b) {
              switch (_b.label) {
                  case 0: return [4 /*yield*/, getInstallationEntry(dependencies.appConfig)];
                  case 1:
                      _a = _b.sent(), installationEntry = _a.installationEntry, registrationPromise = _a.registrationPromise;
                      if (registrationPromise) {
                          registrationPromise.catch(console.error);
                      }
                      else {
                          // If the installation is already registered, update the authentication
                          // token if needed.
                          refreshAuthToken(dependencies).catch(console.error);
                      }
                      return [2 /*return*/, installationEntry.fid];
              }
          });
      });
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  function getToken(dependencies, forceRefresh) {
      if (forceRefresh === void 0) { forceRefresh = false; }
      return __awaiter(this, void 0, void 0, function () {
          var authToken;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0: return [4 /*yield*/, completeInstallationRegistration(dependencies.appConfig)];
                  case 1:
                      _a.sent();
                      return [4 /*yield*/, refreshAuthToken(dependencies, forceRefresh)];
                  case 2:
                      authToken = _a.sent();
                      return [2 /*return*/, authToken.token];
              }
          });
      });
  }
  function completeInstallationRegistration(appConfig) {
      return __awaiter(this, void 0, void 0, function () {
          var registrationPromise;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0: return [4 /*yield*/, getInstallationEntry(appConfig)];
                  case 1:
                      registrationPromise = (_a.sent()).registrationPromise;
                      if (!registrationPromise) return [3 /*break*/, 3];
                      // A createInstallation request is in progress. Wait until it finishes.
                      return [4 /*yield*/, registrationPromise];
                  case 2:
                      // A createInstallation request is in progress. Wait until it finishes.
                      _a.sent();
                      _a.label = 3;
                  case 3: return [2 /*return*/];
              }
          });
      });
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  function deleteInstallationRequest(appConfig, installationEntry) {
      return __awaiter(this, void 0, void 0, function () {
          var endpoint, headers, request, response;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      endpoint = getDeleteEndpoint(appConfig, installationEntry);
                      headers = getHeadersWithAuth(appConfig, installationEntry);
                      request = {
                          method: 'DELETE',
                          headers: headers
                      };
                      return [4 /*yield*/, retryIfServerError(function () { return fetch(endpoint, request); })];
                  case 1:
                      response = _a.sent();
                      if (!!response.ok) return [3 /*break*/, 3];
                      return [4 /*yield*/, getErrorFromResponse('Delete Installation', response)];
                  case 2: throw _a.sent();
                  case 3: return [2 /*return*/];
              }
          });
      });
  }
  function getDeleteEndpoint(appConfig, _a) {
      var fid = _a.fid;
      return getInstallationsEndpoint(appConfig) + "/" + fid;
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  function deleteInstallation(dependencies) {
      return __awaiter(this, void 0, void 0, function () {
          var appConfig, entry;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      appConfig = dependencies.appConfig;
                      return [4 /*yield*/, update(appConfig, function (oldEntry) {
                              if (oldEntry && oldEntry.registrationStatus === 0 /* NOT_STARTED */) {
                                  // Delete the unregistered entry without sending a deleteInstallation request.
                                  return undefined;
                              }
                              return oldEntry;
                          })];
                  case 1:
                      entry = _a.sent();
                      if (!entry) return [3 /*break*/, 6];
                      if (!(entry.registrationStatus === 1 /* IN_PROGRESS */)) return [3 /*break*/, 2];
                      // Can't delete while trying to register.
                      throw ERROR_FACTORY.create("delete-pending-registration" /* DELETE_PENDING_REGISTRATION */);
                  case 2:
                      if (!(entry.registrationStatus === 2 /* COMPLETED */)) return [3 /*break*/, 6];
                      if (!!navigator.onLine) return [3 /*break*/, 3];
                      throw ERROR_FACTORY.create("app-offline" /* APP_OFFLINE */);
                  case 3: return [4 /*yield*/, deleteInstallationRequest(appConfig, entry)];
                  case 4:
                      _a.sent();
                      return [4 /*yield*/, remove(appConfig)];
                  case 5:
                      _a.sent();
                      _a.label = 6;
                  case 6: return [2 /*return*/];
              }
          });
      });
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /**
   * Sets a new callback that will get called when Installation ID changes.
   * Returns an unsubscribe function that will remove the callback when called.
   */
  function onIdChange(_a, callback) {
      var appConfig = _a.appConfig;
      addCallback(appConfig, callback);
      return function () {
          removeCallback(appConfig, callback);
      };
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  function extractAppConfig(app) {
      var e_1, _a;
      if (!app || !app.options) {
          throw getMissingValueError('App Configuration');
      }
      if (!app.name) {
          throw getMissingValueError('App Name');
      }
      // Required app config keys
      var configKeys = [
          'projectId',
          'apiKey',
          'appId'
      ];
      try {
          for (var configKeys_1 = __values(configKeys), configKeys_1_1 = configKeys_1.next(); !configKeys_1_1.done; configKeys_1_1 = configKeys_1.next()) {
              var keyName = configKeys_1_1.value;
              if (!app.options[keyName]) {
                  throw getMissingValueError(keyName);
              }
          }
      }
      catch (e_1_1) { e_1 = { error: e_1_1 }; }
      finally {
          try {
              if (configKeys_1_1 && !configKeys_1_1.done && (_a = configKeys_1.return)) _a.call(configKeys_1);
          }
          finally { if (e_1) throw e_1.error; }
      }
      return {
          appName: app.name,
          projectId: app.options.projectId,
          apiKey: app.options.apiKey,
          appId: app.options.appId
      };
  }
  function getMissingValueError(valueName) {
      return ERROR_FACTORY.create("missing-app-config-values" /* MISSING_APP_CONFIG_VALUES */, {
          valueName: valueName
      });
  }
  
  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  function registerInstallations(instance) {
      var installationsName = 'installations';
      instance.INTERNAL.registerComponent(new Component(installationsName, function (container) {
          var app = container.getProvider('app').getImmediate();
          // Throws if app isn't configured properly.
          var appConfig = extractAppConfig(app);
          var platformLoggerProvider = container.getProvider('platform-logger');
          var dependencies = {
              appConfig: appConfig,
              platformLoggerProvider: platformLoggerProvider
          };
          var installations = {
              app: app,
              getId: function () { return getId(dependencies); },
              getToken: function (forceRefresh) {
                  return getToken(dependencies, forceRefresh);
              },
              delete: function () { return deleteInstallation(dependencies); },
              onIdChange: function (callback) {
                  return onIdChange(dependencies, callback);
              }
          };
          return installations;
      }, "PUBLIC" /* PUBLIC */));
      instance.registerVersion(name, version);
  }
  registerInstallations(firebase);
  
  export { registerInstallations };
  //# sourceMappingURL=index.esm.js.map
  