text: import { getApp, _getProvider, _registerComponent, registerVersion } from '@firebase/app';
  import { Component } from '@firebase/component';
  import { ErrorFactory, FirebaseError } from '@firebase/util';
  import { openDb } from 'idb';
  
  const name = "@firebase/installations";
  const version = "0.5.4";
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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.
   */
  const PENDING_TIMEOUT_MS = 10000;
  const PACKAGE_VERSION = `w:${version}`;
  const INTERNAL_AUTH_VERSION = 'FIS_v2';
  const INSTALLATIONS_API_URL = 'https://firebaseinstallations.googleapis.com/v1';
  const TOKEN_EXPIRATION_BUFFER = 60 * 60 * 1000; // One hour
  const SERVICE = 'installations';
  const SERVICE_NAME = 'Installations';
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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.
   */
  const ERROR_DESCRIPTION_MAP = {
      ["missing-app-config-values" /* MISSING_APP_CONFIG_VALUES */]: 'Missing App configuration value: "{$valueName}"',
      ["not-registered" /* NOT_REGISTERED */]: 'Firebase Installation is not registered.',
      ["installation-not-found" /* INSTALLATION_NOT_FOUND */]: 'Firebase Installation not found.',
      ["request-failed" /* REQUEST_FAILED */]: '{$requestName} request failed with error "{$serverCode} {$serverStatus}: {$serverMessage}"',
      ["app-offline" /* APP_OFFLINE */]: 'Could not process request. Application offline.',
      ["delete-pending-registration" /* DELETE_PENDING_REGISTRATION */]: "Can't delete installation while there is a pending registration request."
  };
  const 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 LLC
   *
   * 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({ 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()
      };
  }
  async function getErrorFromResponse(requestName, response) {
      const responseJson = await response.json();
      const errorData = responseJson.error;
      return ERROR_FACTORY.create("request-failed" /* REQUEST_FAILED */, {
          requestName,
          serverCode: errorData.code,
          serverMessage: errorData.message,
          serverStatus: errorData.status
      });
  }
  function getHeaders({ apiKey }) {
      return new Headers({
          'Content-Type': 'application/json',
          Accept: 'application/json',
          'x-goog-api-key': apiKey
      });
  }
  function getHeadersWithAuth(appConfig, { refreshToken }) {
      const 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.
   */
  async function retryIfServerError(fn) {
      const result = await fn();
      if (result.status >= 500 && result.status < 600) {
          // Internal Server Error. Retry request.
          return fn();
      }
      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 LLC
   *
   * 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.
   */
  async function createInstallationRequest(appConfig, { fid }) {
      const endpoint = getInstallationsEndpoint(appConfig);
      const headers = getHeaders(appConfig);
      const body = {
          fid,
          authVersion: INTERNAL_AUTH_VERSION,
          appId: appConfig.appId,
          sdkVersion: PACKAGE_VERSION
      };
      const request = {
          method: 'POST',
          headers,
          body: JSON.stringify(body)
      };
      const response = await retryIfServerError(() => fetch(endpoint, request));
      if (response.ok) {
          const responseValue = await response.json();
          const registeredInstallationEntry = {
              fid: responseValue.fid || fid,
              registrationStatus: 2 /* COMPLETED */,
              refreshToken: responseValue.refreshToken,
              authToken: extractAuthTokenInfoFromResponse(responseValue.authToken)
          };
          return registeredInstallationEntry;
      }
      else {
          throw await getErrorFromResponse('Create Installation', response);
      }
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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(resolve => {
          setTimeout(resolve, ms);
      });
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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) {
      const b64 = btoa(String.fromCharCode(...array));
      return b64.replace(/\+/g, '-').replace(/\//g, '_');
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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.
   */
  const VALID_FID_PATTERN = /^[cdef][\w-]{21}$/;
  const 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.
          const fidByteArray = new Uint8Array(17);
          const crypto = self.crypto || self.msCrypto;
          crypto.getRandomValues(fidByteArray);
          // Replace the first 4 random bits with the constant FID header of 0b0111.
          fidByteArray[0] = 0b01110000 + (fidByteArray[0] % 0b00010000);
          const 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) {
      const 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 LLC
   *
   * 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 LLC
   *
   * 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.
   */
  const fidChangeCallbacks = new Map();
  /**
   * Calls the onIdChange callbacks with the new FID value, and broadcasts the
   * change to other tabs.
   */
  function fidChanged(appConfig, fid) {
      const 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();
      const key = getKey(appConfig);
      let callbackSet = fidChangeCallbacks.get(key);
      if (!callbackSet) {
          callbackSet = new Set();
          fidChangeCallbacks.set(key, callbackSet);
      }
      callbackSet.add(callback);
  }
  function removeCallback(appConfig, callback) {
      const key = getKey(appConfig);
      const 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) {
      const callbacks = fidChangeCallbacks.get(key);
      if (!callbacks) {
          return;
      }
      for (const callback of callbacks) {
          callback(fid);
      }
  }
  function broadcastFidChange(key, fid) {
      const channel = getBroadcastChannel();
      if (channel) {
          channel.postMessage({ key, fid });
      }
      closeBroadcastChannel();
  }
  let 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 = 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 LLC
   *
   * 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.
   */
  const DATABASE_NAME = 'firebase-installations-database';
  const DATABASE_VERSION = 1;
  const OBJECT_STORE_NAME = 'firebase-installations-store';
  let dbPromise = null;
  function getDbPromise() {
      if (!dbPromise) {
          dbPromise = openDb(DATABASE_NAME, DATABASE_VERSION, 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. */
  async function set(appConfig, value) {
      const key = getKey(appConfig);
      const db = await getDbPromise();
      const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
      const objectStore = tx.objectStore(OBJECT_STORE_NAME);
      const oldValue = await objectStore.get(key);
      await objectStore.put(value, key);
      await tx.complete;
      if (!oldValue || oldValue.fid !== value.fid) {
          fidChanged(appConfig, value.fid);
      }
      return value;
  }
  /** Removes record(s) from the objectStore that match the given key. */
  async function remove(appConfig) {
      const key = getKey(appConfig);
      const db = await getDbPromise();
      const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
      await tx.objectStore(OBJECT_STORE_NAME).delete(key);
      await tx.complete;
  }
  /**
   * 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
   */
  async function update(appConfig, updateFn) {
      const key = getKey(appConfig);
      const db = await getDbPromise();
      const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
      const store = tx.objectStore(OBJECT_STORE_NAME);
      const oldValue = await store.get(key);
      const newValue = updateFn(oldValue);
      if (newValue === undefined) {
          await store.delete(key);
      }
      else {
          await store.put(newValue, key);
      }
      await tx.complete;
      if (newValue && (!oldValue || oldValue.fid !== newValue.fid)) {
          fidChanged(appConfig, newValue.fid);
      }
      return newValue;
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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.
   */
  async function getInstallationEntry(appConfig) {
      let registrationPromise;
      const installationEntry = await update(appConfig, oldEntry => {
          const installationEntry = updateOrCreateInstallationEntry(oldEntry);
          const entryWithPromise = triggerRegistrationIfNecessary(appConfig, installationEntry);
          registrationPromise = entryWithPromise.registrationPromise;
          return entryWithPromise.installationEntry;
      });
      if (installationEntry.fid === INVALID_FID) {
          // FID generation failed. Waiting for the FID from the server.
          return { installationEntry: await registrationPromise };
      }
      return {
          installationEntry,
          registrationPromise
      };
  }
  /**
   * Creates a new Installation Entry if one does not exist.
   * Also clears timed out pending requests.
   */
  function updateOrCreateInstallationEntry(oldEntry) {
      const 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.
              const registrationPromiseWithError = Promise.reject(ERROR_FACTORY.create("app-offline" /* APP_OFFLINE */));
              return {
                  installationEntry,
                  registrationPromise: registrationPromiseWithError
              };
          }
          // Try registering. Change status to IN_PROGRESS.
          const inProgressEntry = {
              fid: installationEntry.fid,
              registrationStatus: 1 /* IN_PROGRESS */,
              registrationTime: Date.now()
          };
          const registrationPromise = registerInstallation(appConfig, inProgressEntry);
          return { installationEntry: inProgressEntry, registrationPromise };
      }
      else if (installationEntry.registrationStatus === 1 /* IN_PROGRESS */) {
          return {
              installationEntry,
              registrationPromise: waitUntilFidRegistration(appConfig)
          };
      }
      else {
          return { installationEntry };
      }
  }
  /** This will be executed only once for each new Firebase Installation. */
  async function registerInstallation(appConfig, installationEntry) {
      try {
          const registeredInstallationEntry = await createInstallationRequest(appConfig, installationEntry);
          return set(appConfig, registeredInstallationEntry);
      }
      catch (e) {
          if (isServerError(e) && e.customData.serverCode === 409) {
              // Server returned a "FID can not be used" error.
              // Generate a new ID next time.
              await remove(appConfig);
          }
          else {
              // Registration failed. Set FID as not registered.
              await set(appConfig, {
                  fid: installationEntry.fid,
                  registrationStatus: 0 /* NOT_STARTED */
              });
          }
          throw e;
      }
  }
  /** Call if FID registration is pending in another request. */
  async function waitUntilFidRegistration(appConfig) {
      // Unfortunately, there is no way of reliably observing when a value in
      // IndexedDB changes (yet, see https://github.com/WICG/indexed-db-observers),
      // so we need to poll.
      let entry = await updateInstallationRequest(appConfig);
      while (entry.registrationStatus === 1 /* IN_PROGRESS */) {
          // createInstallation request still in progress.
          await sleep(100);
          entry = await updateInstallationRequest(appConfig);
      }
      if (entry.registrationStatus === 0 /* NOT_STARTED */) {
          // The request timed out or failed in a different call. Try again.
          const { installationEntry, registrationPromise } = await getInstallationEntry(appConfig);
          if (registrationPromise) {
              return registrationPromise;
          }
          else {
              // if there is no registrationPromise, entry is registered.
              return installationEntry;
          }
      }
      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, 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 LLC
   *
   * 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.
   */
  async function generateAuthTokenRequest({ appConfig, platformLoggerProvider }, installationEntry) {
      const endpoint = getGenerateAuthTokenEndpoint(appConfig, installationEntry);
      const headers = getHeadersWithAuth(appConfig, installationEntry);
      // If platform logger exists, add the platform info string to the header.
      const platformLogger = platformLoggerProvider.getImmediate({
          optional: true
      });
      if (platformLogger) {
          headers.append('x-firebase-client', platformLogger.getPlatformInfoString());
      }
      const body = {
          installation: {
              sdkVersion: PACKAGE_VERSION
          }
      };
      const request = {
          method: 'POST',
          headers,
          body: JSON.stringify(body)
      };
      const response = await retryIfServerError(() => fetch(endpoint, request));
      if (response.ok) {
          const responseValue = await response.json();
          const completedAuthToken = extractAuthTokenInfoFromResponse(responseValue);
          return completedAuthToken;
      }
      else {
          throw await getErrorFromResponse('Generate Auth Token', response);
      }
  }
  function getGenerateAuthTokenEndpoint(appConfig, { fid }) {
      return `${getInstallationsEndpoint(appConfig)}/${fid}/authTokens:generate`;
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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.
   */
  async function refreshAuthToken(installations, forceRefresh = false) {
      let tokenPromise;
      const entry = await update(installations.appConfig, oldEntry => {
          if (!isEntryRegistered(oldEntry)) {
              throw ERROR_FACTORY.create("not-registered" /* NOT_REGISTERED */);
          }
          const 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(installations, forceRefresh);
              return oldEntry;
          }
          else {
              // No token or token expired.
              if (!navigator.onLine) {
                  throw ERROR_FACTORY.create("app-offline" /* APP_OFFLINE */);
              }
              const inProgressEntry = makeAuthTokenRequestInProgressEntry(oldEntry);
              tokenPromise = fetchAuthTokenFromServer(installations, inProgressEntry);
              return inProgressEntry;
          }
      });
      const authToken = tokenPromise
          ? await tokenPromise
          : entry.authToken;
      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.
   */
  async function waitUntilAuthTokenRequest(installations, forceRefresh) {
      // Unfortunately, there is no way of reliably observing when a value in
      // IndexedDB changes (yet, see https://github.com/WICG/indexed-db-observers),
      // so we need to poll.
      let entry = await updateAuthTokenRequest(installations.appConfig);
      while (entry.authToken.requestStatus === 1 /* IN_PROGRESS */) {
          // generateAuthToken still in progress.
          await sleep(100);
          entry = await updateAuthTokenRequest(installations.appConfig);
      }
      const authToken = entry.authToken;
      if (authToken.requestStatus === 0 /* NOT_STARTED */) {
          // The request timed out or failed in a different call. Try again.
          return refreshAuthToken(installations, forceRefresh);
      }
      else {
          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, oldEntry => {
          if (!isEntryRegistered(oldEntry)) {
              throw ERROR_FACTORY.create("not-registered" /* NOT_REGISTERED */);
          }
          const oldAuthToken = oldEntry.authToken;
          if (hasAuthTokenRequestTimedOut(oldAuthToken)) {
              return Object.assign(Object.assign({}, oldEntry), { authToken: { requestStatus: 0 /* NOT_STARTED */ } });
          }
          return oldEntry;
      });
  }
  async function fetchAuthTokenFromServer(installations, installationEntry) {
      try {
          const authToken = await generateAuthTokenRequest(installations, installationEntry);
          const updatedInstallationEntry = Object.assign(Object.assign({}, installationEntry), { authToken });
          await set(installations.appConfig, updatedInstallationEntry);
          return authToken;
      }
      catch (e) {
          if (isServerError(e) &&
              (e.customData.serverCode === 401 || e.customData.serverCode === 404)) {
              // Server returned a "FID not found" or a "Invalid authentication" error.
              // Generate a new ID next time.
              await remove(installations.appConfig);
          }
          else {
              const updatedInstallationEntry = Object.assign(Object.assign({}, installationEntry), { authToken: { requestStatus: 0 /* NOT_STARTED */ } });
              await set(installations.appConfig, updatedInstallationEntry);
          }
          throw e;
      }
  }
  function isEntryRegistered(installationEntry) {
      return (installationEntry !== undefined &&
          installationEntry.registrationStatus === 2 /* COMPLETED */);
  }
  function isAuthTokenValid(authToken) {
      return (authToken.requestStatus === 2 /* COMPLETED */ &&
          !isAuthTokenExpired(authToken));
  }
  function isAuthTokenExpired(authToken) {
      const 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) {
      const inProgressAuthToken = {
          requestStatus: 1 /* IN_PROGRESS */,
          requestTime: Date.now()
      };
      return Object.assign(Object.assign({}, oldEntry), { authToken: inProgressAuthToken });
  }
  function hasAuthTokenRequestTimedOut(authToken) {
      return (authToken.requestStatus === 1 /* IN_PROGRESS */ &&
          authToken.requestTime + PENDING_TIMEOUT_MS < Date.now());
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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.
   */
  /**
   * Creates a Firebase Installation if there isn't one for the app and
   * returns the Installation ID.
   * @param installations - The `Installations` instance.
   *
   * @public
   */
  async function getId(installations) {
      const installationsImpl = installations;
      const { installationEntry, registrationPromise } = await getInstallationEntry(installationsImpl.appConfig);
      if (registrationPromise) {
          registrationPromise.catch(console.error);
      }
      else {
          // If the installation is already registered, update the authentication
          // token if needed.
          refreshAuthToken(installationsImpl).catch(console.error);
      }
      return installationEntry.fid;
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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 Firebase Installations auth token, identifying the current
   * Firebase Installation.
   * @param installations - The `Installations` instance.
   * @param forceRefresh - Force refresh regardless of token expiration.
   *
   * @public
   */
  async function getToken(installations, forceRefresh = false) {
      const installationsImpl = installations;
      await completeInstallationRegistration(installationsImpl.appConfig);
      // At this point we either have a Registered Installation in the DB, or we've
      // already thrown an error.
      const authToken = await refreshAuthToken(installationsImpl, forceRefresh);
      return authToken.token;
  }
  async function completeInstallationRegistration(appConfig) {
      const { registrationPromise } = await getInstallationEntry(appConfig);
      if (registrationPromise) {
          // A createInstallation request is in progress. Wait until it finishes.
          await registrationPromise;
      }
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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.
   */
  async function deleteInstallationRequest(appConfig, installationEntry) {
      const endpoint = getDeleteEndpoint(appConfig, installationEntry);
      const headers = getHeadersWithAuth(appConfig, installationEntry);
      const request = {
          method: 'DELETE',
          headers
      };
      const response = await retryIfServerError(() => fetch(endpoint, request));
      if (!response.ok) {
          throw await getErrorFromResponse('Delete Installation', response);
      }
  }
  function getDeleteEndpoint(appConfig, { fid }) {
      return `${getInstallationsEndpoint(appConfig)}/${fid}`;
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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.
   */
  /**
   * Deletes the Firebase Installation and all associated data.
   * @param installations - The `Installations` instance.
   *
   * @public
   */
  async function deleteInstallations(installations) {
      const { appConfig } = installations;
      const entry = await update(appConfig, oldEntry => {
          if (oldEntry && oldEntry.registrationStatus === 0 /* NOT_STARTED */) {
              // Delete the unregistered entry without sending a deleteInstallation request.
              return undefined;
          }
          return oldEntry;
      });
      if (entry) {
          if (entry.registrationStatus === 1 /* IN_PROGRESS */) {
              // Can't delete while trying to register.
              throw ERROR_FACTORY.create("delete-pending-registration" /* DELETE_PENDING_REGISTRATION */);
          }
          else if (entry.registrationStatus === 2 /* COMPLETED */) {
              if (!navigator.onLine) {
                  throw ERROR_FACTORY.create("app-offline" /* APP_OFFLINE */);
              }
              else {
                  await deleteInstallationRequest(appConfig, entry);
                  await remove(appConfig);
              }
          }
      }
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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.
   * @param installations - The `Installations` instance.
   * @param callback - The callback function that is invoked when FID changes.
   * @returns A function that can be called to unsubscribe.
   *
   * @public
   */
  function onIdChange(installations, callback) {
      const { appConfig } = installations;
      addCallback(appConfig, callback);
      return () => {
          removeCallback(appConfig, callback);
      };
  }
  
  /**
   * @license
   * Copyright 2020 Google LLC
   *
   * 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 an instance of {@link Installations} associated with the given
   * {@link @firebase/app#FirebaseApp} instance.
   * @param app - The {@link @firebase/app#FirebaseApp} instance.
   *
   * @public
   */
  function getInstallations(app = getApp()) {
      const installationsImpl = _getProvider(app, 'installations').getImmediate();
      return installationsImpl;
  }
  
  /**
   * @license
   * Copyright 2019 Google LLC
   *
   * 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) {
      if (!app || !app.options) {
          throw getMissingValueError('App Configuration');
      }
      if (!app.name) {
          throw getMissingValueError('App Name');
      }
      // Required app config keys
      const configKeys = [
          'projectId',
          'apiKey',
          'appId'
      ];
      for (const keyName of configKeys) {
          if (!app.options[keyName]) {
              throw getMissingValueError(keyName);
          }
      }
      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
      });
  }
  
  /**
   * @license
   * Copyright 2020 Google LLC
   *
   * 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.
   */
  const INSTALLATIONS_NAME = 'installations';
  const INSTALLATIONS_NAME_INTERNAL = 'installations-internal';
  const publicFactory = (container) => {
      const app = container.getProvider('app').getImmediate();
      // Throws if app isn't configured properly.
      const appConfig = extractAppConfig(app);
      const platformLoggerProvider = _getProvider(app, 'platform-logger');
      const installationsImpl = {
          app,
          appConfig,
          platformLoggerProvider,
          _delete: () => Promise.resolve()
      };
      return installationsImpl;
  };
  const internalFactory = (container) => {
      const app = container.getProvider('app').getImmediate();
      // Internal FIS instance relies on public FIS instance.
      const installations = _getProvider(app, INSTALLATIONS_NAME).getImmediate();
      const installationsInternal = {
          getId: () => getId(installations),
          getToken: (forceRefresh) => getToken(installations, forceRefresh)
      };
      return installationsInternal;
  };
  function registerInstallations() {
      _registerComponent(new Component(INSTALLATIONS_NAME, publicFactory, "PUBLIC" /* PUBLIC */));
      _registerComponent(new Component(INSTALLATIONS_NAME_INTERNAL, internalFactory, "PRIVATE" /* PRIVATE */));
  }
  
  /**
   * Firebase Installations
   *
   * @packageDocumentation
   */
  registerInstallations();
  registerVersion(name, version);
  // BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation
  registerVersion(name, version, 'esm2017');
  
  export { deleteInstallations, getId, getInstallations, getToken, onIdChange };
  //# sourceMappingURL=index.esm2017.js.map
  