text: import { getApp, _getProvider, _registerComponent, registerVersion, SDK_VERSION } from '@firebase/app';
  import { FirebaseError, getModularInstance } from '@firebase/util';
  import { Component } from '@firebase/component';
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * Error codes for requests made by the the XhrIo wrapper.
   */
  var ErrorCode;
  (function (ErrorCode) {
      ErrorCode[ErrorCode["NO_ERROR"] = 0] = "NO_ERROR";
      ErrorCode[ErrorCode["NETWORK_ERROR"] = 1] = "NETWORK_ERROR";
      ErrorCode[ErrorCode["ABORT"] = 2] = "ABORT";
  })(ErrorCode || (ErrorCode = {}));
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * @fileoverview Constants used in the Firebase Storage library.
   */
  /**
   * Domain name for firebase storage.
   */
  const DEFAULT_HOST = 'firebasestorage.googleapis.com';
  /**
   * The key in Firebase config json for the storage bucket.
   */
  const CONFIG_STORAGE_BUCKET_KEY = 'storageBucket';
  /**
   * 2 minutes
   *
   * The timeout for all operations except upload.
   */
  const DEFAULT_MAX_OPERATION_RETRY_TIME = 2 * 60 * 1000;
  /**
   * 10 minutes
   *
   * The timeout for upload.
   */
  const DEFAULT_MAX_UPLOAD_RETRY_TIME = 10 * 60 * 1000;
  
  /**
   * An error returned by the Firebase Storage SDK.
   * @public
   */
  class FirebaseStorageError extends FirebaseError {
      /**
       * @param code - A StorageErrorCode string to be prefixed with 'storage/' and
       *  added to the end of the message.
       * @param message  - Error message.
       */
      constructor(code, message) {
          super(prependCode(code), `Firebase Storage: ${message} (${prependCode(code)})`);
          /**
           * Stores custom error data unque to FirebaseStorageError.
           */
          this.customData = { serverResponse: null };
          this._baseMessage = this.message;
          // Without this, `instanceof FirebaseStorageError`, in tests for example,
          // returns false.
          Object.setPrototypeOf(this, FirebaseStorageError.prototype);
      }
      /**
       * Compares a StorageErrorCode against this error's code, filtering out the prefix.
       */
      _codeEquals(code) {
          return prependCode(code) === this.code;
      }
      /**
       * Optional response message that was added by the server.
       */
      get serverResponse() {
          return this.customData.serverResponse;
      }
      set serverResponse(serverResponse) {
          this.customData.serverResponse = serverResponse;
          if (this.customData.serverResponse) {
              this.message = `${this._baseMessage}\n${this.customData.serverResponse}`;
          }
          else {
              this.message = this._baseMessage;
          }
      }
  }
  function prependCode(code) {
      return 'storage/' + code;
  }
  function unknown() {
      const message = 'An unknown error occurred, please check the error payload for ' +
          'server response.';
      return new FirebaseStorageError("unknown" /* UNKNOWN */, message);
  }
  function objectNotFound(path) {
      return new FirebaseStorageError("object-not-found" /* OBJECT_NOT_FOUND */, "Object '" + path + "' does not exist.");
  }
  function quotaExceeded(bucket) {
      return new FirebaseStorageError("quota-exceeded" /* QUOTA_EXCEEDED */, "Quota for bucket '" +
          bucket +
          "' exceeded, please view quota on " +
          'https://firebase.google.com/pricing/.');
  }
  function unauthenticated() {
      const message = 'User is not authenticated, please authenticate using Firebase ' +
          'Authentication and try again.';
      return new FirebaseStorageError("unauthenticated" /* UNAUTHENTICATED */, message);
  }
  function unauthorizedApp() {
      return new FirebaseStorageError("unauthorized-app" /* UNAUTHORIZED_APP */, 'This app does not have permission to access Firebase Storage on this project.');
  }
  function unauthorized(path) {
      return new FirebaseStorageError("unauthorized" /* UNAUTHORIZED */, "User does not have permission to access '" + path + "'.");
  }
  function retryLimitExceeded() {
      return new FirebaseStorageError("retry-limit-exceeded" /* RETRY_LIMIT_EXCEEDED */, 'Max retry time for operation exceeded, please try again.');
  }
  function canceled() {
      return new FirebaseStorageError("canceled" /* CANCELED */, 'User canceled the upload/download.');
  }
  function invalidUrl(url) {
      return new FirebaseStorageError("invalid-url" /* INVALID_URL */, "Invalid URL '" + url + "'.");
  }
  function invalidDefaultBucket(bucket) {
      return new FirebaseStorageError("invalid-default-bucket" /* INVALID_DEFAULT_BUCKET */, "Invalid default bucket '" + bucket + "'.");
  }
  function noDefaultBucket() {
      return new FirebaseStorageError("no-default-bucket" /* NO_DEFAULT_BUCKET */, 'No default bucket ' +
          "found. Did you set the '" +
          CONFIG_STORAGE_BUCKET_KEY +
          "' property when initializing the app?");
  }
  function cannotSliceBlob() {
      return new FirebaseStorageError("cannot-slice-blob" /* CANNOT_SLICE_BLOB */, 'Cannot slice blob for upload. Please retry the upload.');
  }
  function serverFileWrongSize() {
      return new FirebaseStorageError("server-file-wrong-size" /* SERVER_FILE_WRONG_SIZE */, 'Server recorded incorrect upload file size, please retry the upload.');
  }
  function noDownloadURL() {
      return new FirebaseStorageError("no-download-url" /* NO_DOWNLOAD_URL */, 'The given file does not have any download URLs.');
  }
  function invalidArgument(message) {
      return new FirebaseStorageError("invalid-argument" /* INVALID_ARGUMENT */, message);
  }
  function appDeleted() {
      return new FirebaseStorageError("app-deleted" /* APP_DELETED */, 'The Firebase app was deleted.');
  }
  /**
   * @param name - The name of the operation that was invalid.
   */
  function invalidRootOperation(name) {
      return new FirebaseStorageError("invalid-root-operation" /* INVALID_ROOT_OPERATION */, "The operation '" +
          name +
          "' cannot be performed on a root reference, create a non-root " +
          "reference using child, such as .child('file.png').");
  }
  /**
   * @param format - The format that was not valid.
   * @param message - A message describing the format violation.
   */
  function invalidFormat(format, message) {
      return new FirebaseStorageError("invalid-format" /* INVALID_FORMAT */, "String does not match format '" + format + "': " + message);
  }
  /**
   * @param message - A message describing the internal error.
   */
  function internalError(message) {
      throw new FirebaseStorageError("internal-error" /* INTERNAL_ERROR */, 'Internal error: ' + message);
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * We use this instead of goog.net.XhrIo because goog.net.XhrIo is hyuuuuge and
   * doesn't work in React Native on Android.
   */
  class NetworkXhrIo {
      constructor() {
          this.sent_ = false;
          this.xhr_ = new XMLHttpRequest();
          this.errorCode_ = ErrorCode.NO_ERROR;
          this.sendPromise_ = new Promise(resolve => {
              this.xhr_.addEventListener('abort', () => {
                  this.errorCode_ = ErrorCode.ABORT;
                  resolve(this);
              });
              this.xhr_.addEventListener('error', () => {
                  this.errorCode_ = ErrorCode.NETWORK_ERROR;
                  resolve(this);
              });
              this.xhr_.addEventListener('load', () => {
                  resolve(this);
              });
          });
      }
      /**
       * @override
       */
      send(url, method, body, headers) {
          if (this.sent_) {
              throw internalError('cannot .send() more than once');
          }
          this.sent_ = true;
          this.xhr_.open(method, url, true);
          if (headers !== undefined) {
              for (const key in headers) {
                  if (headers.hasOwnProperty(key)) {
                      this.xhr_.setRequestHeader(key, headers[key].toString());
                  }
              }
          }
          if (body !== undefined) {
              this.xhr_.send(body);
          }
          else {
              this.xhr_.send();
          }
          return this.sendPromise_;
      }
      /**
       * @override
       */
      getErrorCode() {
          if (!this.sent_) {
              throw internalError('cannot .getErrorCode() before sending');
          }
          return this.errorCode_;
      }
      /**
       * @override
       */
      getStatus() {
          if (!this.sent_) {
              throw internalError('cannot .getStatus() before sending');
          }
          try {
              return this.xhr_.status;
          }
          catch (e) {
              return -1;
          }
      }
      /**
       * @override
       */
      getResponseText() {
          if (!this.sent_) {
              throw internalError('cannot .getResponseText() before sending');
          }
          return this.xhr_.responseText;
      }
      /**
       * Aborts the request.
       * @override
       */
      abort() {
          this.xhr_.abort();
      }
      /**
       * @override
       */
      getResponseHeader(header) {
          return this.xhr_.getResponseHeader(header);
      }
      /**
       * @override
       */
      addUploadProgressListener(listener) {
          if (this.xhr_.upload != null) {
              this.xhr_.upload.addEventListener('progress', listener);
          }
      }
      /**
       * @override
       */
      removeUploadProgressListener(listener) {
          if (this.xhr_.upload != null) {
              this.xhr_.upload.removeEventListener('progress', listener);
          }
      }
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * Factory-like class for creating XhrIo instances.
   */
  class XhrIoPool {
      createXhrIo() {
          return new NetworkXhrIo();
      }
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * Firebase Storage location data.
   *
   * @internal
   */
  class Location {
      constructor(bucket, path) {
          this.bucket = bucket;
          this.path_ = path;
      }
      get path() {
          return this.path_;
      }
      get isRoot() {
          return this.path.length === 0;
      }
      fullServerUrl() {
          const encode = encodeURIComponent;
          return '/b/' + encode(this.bucket) + '/o/' + encode(this.path);
      }
      bucketOnlyServerUrl() {
          const encode = encodeURIComponent;
          return '/b/' + encode(this.bucket) + '/o';
      }
      static makeFromBucketSpec(bucketString, host) {
          let bucketLocation;
          try {
              bucketLocation = Location.makeFromUrl(bucketString, host);
          }
          catch (e) {
              // Not valid URL, use as-is. This lets you put bare bucket names in
              // config.
              return new Location(bucketString, '');
          }
          if (bucketLocation.path === '') {
              return bucketLocation;
          }
          else {
              throw invalidDefaultBucket(bucketString);
          }
      }
      static makeFromUrl(url, host) {
          let location = null;
          const bucketDomain = '([A-Za-z0-9.\\-_]+)';
          function gsModify(loc) {
              if (loc.path.charAt(loc.path.length - 1) === '/') {
                  loc.path_ = loc.path_.slice(0, -1);
              }
          }
          const gsPath = '(/(.*))?$';
          const gsRegex = new RegExp('^gs://' + bucketDomain + gsPath, 'i');
          const gsIndices = { bucket: 1, path: 3 };
          function httpModify(loc) {
              loc.path_ = decodeURIComponent(loc.path);
          }
          const version = 'v[A-Za-z0-9_]+';
          const firebaseStorageHost = host.replace(/[.]/g, '\\.');
          const firebaseStoragePath = '(/([^?#]*).*)?$';
          const firebaseStorageRegExp = new RegExp(`^https?://${firebaseStorageHost}/${version}/b/${bucketDomain}/o${firebaseStoragePath}`, 'i');
          const firebaseStorageIndices = { bucket: 1, path: 3 };
          const cloudStorageHost = host === DEFAULT_HOST
              ? '(?:storage.googleapis.com|storage.cloud.google.com)'
              : host;
          const cloudStoragePath = '([^?#]*)';
          const cloudStorageRegExp = new RegExp(`^https?://${cloudStorageHost}/${bucketDomain}/${cloudStoragePath}`, 'i');
          const cloudStorageIndices = { bucket: 1, path: 2 };
          const groups = [
              { regex: gsRegex, indices: gsIndices, postModify: gsModify },
              {
                  regex: firebaseStorageRegExp,
                  indices: firebaseStorageIndices,
                  postModify: httpModify
              },
              {
                  regex: cloudStorageRegExp,
                  indices: cloudStorageIndices,
                  postModify: httpModify
              }
          ];
          for (let i = 0; i < groups.length; i++) {
              const group = groups[i];
              const captures = group.regex.exec(url);
              if (captures) {
                  const bucketValue = captures[group.indices.bucket];
                  let pathValue = captures[group.indices.path];
                  if (!pathValue) {
                      pathValue = '';
                  }
                  location = new Location(bucketValue, pathValue);
                  group.postModify(location);
                  break;
              }
          }
          if (location == null) {
              throw invalidUrl(url);
          }
          return location;
      }
  }
  
  /**
   * A request whose promise always fails.
   */
  class FailRequest {
      constructor(error) {
          this.promise_ = Promise.reject(error);
      }
      /** @inheritDoc */
      getPromise() {
          return this.promise_;
      }
      /** @inheritDoc */
      cancel(_appDelete = false) { }
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * @param f May be invoked
   *     before the function returns.
   * @param callback Get all the arguments passed to the function
   *     passed to f, including the initial boolean.
   */
  function start(f, 
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  callback, timeout) {
      // TODO(andysoto): make this code cleaner (probably refactor into an actual
      // type instead of a bunch of functions with state shared in the closure)
      let waitSeconds = 1;
      // Would type this as "number" but that doesn't work for Node so ¯\_(ツ)_/¯
      // TODO: find a way to exclude Node type definition for storage because storage only works in browser
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      let timeoutId = null;
      let hitTimeout = false;
      let cancelState = 0;
      function canceled() {
          return cancelState === 2;
      }
      let triggeredCallback = false;
      function triggerCallback(...args) {
          if (!triggeredCallback) {
              triggeredCallback = true;
              callback.apply(null, args);
          }
      }
      function callWithDelay(millis) {
          timeoutId = setTimeout(() => {
              timeoutId = null;
              f(handler, canceled());
          }, millis);
      }
      function handler(success, ...args) {
          if (triggeredCallback) {
              return;
          }
          if (success) {
              triggerCallback.call(null, success, ...args);
              return;
          }
          const mustStop = canceled() || hitTimeout;
          if (mustStop) {
              triggerCallback.call(null, success, ...args);
              return;
          }
          if (waitSeconds < 64) {
              /* TODO(andysoto): don't back off so quickly if we know we're offline. */
              waitSeconds *= 2;
          }
          let waitMillis;
          if (cancelState === 1) {
              cancelState = 2;
              waitMillis = 0;
          }
          else {
              waitMillis = (waitSeconds + Math.random()) * 1000;
          }
          callWithDelay(waitMillis);
      }
      let stopped = false;
      function stop(wasTimeout) {
          if (stopped) {
              return;
          }
          stopped = true;
          if (triggeredCallback) {
              return;
          }
          if (timeoutId !== null) {
              if (!wasTimeout) {
                  cancelState = 2;
              }
              clearTimeout(timeoutId);
              callWithDelay(0);
          }
          else {
              if (!wasTimeout) {
                  cancelState = 1;
              }
          }
      }
      callWithDelay(0);
      setTimeout(() => {
          hitTimeout = true;
          stop(true);
      }, timeout);
      return stop;
  }
  /**
   * Stops the retry loop from repeating.
   * If the function is currently "in between" retries, it is invoked immediately
   * with the second parameter as "true". Otherwise, it will be invoked once more
   * after the current invocation finishes iff the current invocation would have
   * triggered another retry.
   */
  function stop(id) {
      id(false);
  }
  
  /**
   * @license
   * Copyright 2017 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 isJustDef(p) {
      return p !== void 0;
  }
  // eslint-disable-next-line @typescript-eslint/ban-types
  function isFunction(p) {
      return typeof p === 'function';
  }
  function isNonArrayObject(p) {
      return typeof p === 'object' && !Array.isArray(p);
  }
  function isString(p) {
      return typeof p === 'string' || p instanceof String;
  }
  function isNativeBlob(p) {
      return isNativeBlobDefined() && p instanceof Blob;
  }
  function isNativeBlobDefined() {
      return typeof Blob !== 'undefined';
  }
  function validateNumber(argument, minValue, maxValue, value) {
      if (value < minValue) {
          throw invalidArgument(`Invalid value for '${argument}'. Expected ${minValue} or greater.`);
      }
      if (value > maxValue) {
          throw invalidArgument(`Invalid value for '${argument}'. Expected ${maxValue} or less.`);
      }
  }
  
  /**
   * @license
   * Copyright 2017 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 makeUrl(urlPart, host) {
      const protocolMatch = host.match(/^(\w+):\/\/.+/);
      const protocol = protocolMatch === null || protocolMatch === void 0 ? void 0 : protocolMatch[1];
      let origin = host;
      if (protocol == null) {
          origin = `https://${host}`;
      }
      return `${origin}/v0${urlPart}`;
  }
  function makeQueryString(params) {
      const encode = encodeURIComponent;
      let queryPart = '?';
      for (const key in params) {
          if (params.hasOwnProperty(key)) {
              const nextPart = encode(key) + '=' + encode(params[key]);
              queryPart = queryPart + nextPart + '&';
          }
      }
      // Chop off the extra '&' or '?' on the end
      queryPart = queryPart.slice(0, -1);
      return queryPart;
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  class NetworkRequest {
      constructor(url, method, headers, body, successCodes, additionalRetryCodes, callback, errorCallback, timeout, progressCallback, pool) {
          this.pendingXhr_ = null;
          this.backoffId_ = null;
          this.canceled_ = false;
          this.appDelete_ = false;
          this.url_ = url;
          this.method_ = method;
          this.headers_ = headers;
          this.body_ = body;
          this.successCodes_ = successCodes.slice();
          this.additionalRetryCodes_ = additionalRetryCodes.slice();
          this.callback_ = callback;
          this.errorCallback_ = errorCallback;
          this.progressCallback_ = progressCallback;
          this.timeout_ = timeout;
          this.pool_ = pool;
          this.promise_ = new Promise((resolve, reject) => {
              this.resolve_ = resolve;
              this.reject_ = reject;
              this.start_();
          });
      }
      /**
       * Actually starts the retry loop.
       */
      start_() {
          const self = this;
          function doTheRequest(backoffCallback, canceled) {
              if (canceled) {
                  backoffCallback(false, new RequestEndStatus(false, null, true));
                  return;
              }
              const xhr = self.pool_.createXhrIo();
              self.pendingXhr_ = xhr;
              function progressListener(progressEvent) {
                  const loaded = progressEvent.loaded;
                  const total = progressEvent.lengthComputable ? progressEvent.total : -1;
                  if (self.progressCallback_ !== null) {
                      self.progressCallback_(loaded, total);
                  }
              }
              if (self.progressCallback_ !== null) {
                  xhr.addUploadProgressListener(progressListener);
              }
              // eslint-disable-next-line @typescript-eslint/no-floating-promises
              xhr
                  .send(self.url_, self.method_, self.body_, self.headers_)
                  .then((xhr) => {
                  if (self.progressCallback_ !== null) {
                      xhr.removeUploadProgressListener(progressListener);
                  }
                  self.pendingXhr_ = null;
                  xhr = xhr;
                  const hitServer = xhr.getErrorCode() === ErrorCode.NO_ERROR;
                  const status = xhr.getStatus();
                  if (!hitServer || self.isRetryStatusCode_(status)) {
                      const wasCanceled = xhr.getErrorCode() === ErrorCode.ABORT;
                      backoffCallback(false, new RequestEndStatus(false, null, wasCanceled));
                      return;
                  }
                  const successCode = self.successCodes_.indexOf(status) !== -1;
                  backoffCallback(true, new RequestEndStatus(successCode, xhr));
              });
          }
          /**
           * @param requestWentThrough - True if the request eventually went
           *     through, false if it hit the retry limit or was canceled.
           */
          function backoffDone(requestWentThrough, status) {
              const resolve = self.resolve_;
              const reject = self.reject_;
              const xhr = status.xhr;
              if (status.wasSuccessCode) {
                  try {
                      const result = self.callback_(xhr, xhr.getResponseText());
                      if (isJustDef(result)) {
                          resolve(result);
                      }
                      else {
                          resolve();
                      }
                  }
                  catch (e) {
                      reject(e);
                  }
              }
              else {
                  if (xhr !== null) {
                      const err = unknown();
                      err.serverResponse = xhr.getResponseText();
                      if (self.errorCallback_) {
                          reject(self.errorCallback_(xhr, err));
                      }
                      else {
                          reject(err);
                      }
                  }
                  else {
                      if (status.canceled) {
                          const err = self.appDelete_ ? appDeleted() : canceled();
                          reject(err);
                      }
                      else {
                          const err = retryLimitExceeded();
                          reject(err);
                      }
                  }
              }
          }
          if (this.canceled_) {
              backoffDone(false, new RequestEndStatus(false, null, true));
          }
          else {
              this.backoffId_ = start(doTheRequest, backoffDone, this.timeout_);
          }
      }
      /** @inheritDoc */
      getPromise() {
          return this.promise_;
      }
      /** @inheritDoc */
      cancel(appDelete) {
          this.canceled_ = true;
          this.appDelete_ = appDelete || false;
          if (this.backoffId_ !== null) {
              stop(this.backoffId_);
          }
          if (this.pendingXhr_ !== null) {
              this.pendingXhr_.abort();
          }
      }
      isRetryStatusCode_(status) {
          // The codes for which to retry came from this page:
          // https://cloud.google.com/storage/docs/exponential-backoff
          const isFiveHundredCode = status >= 500 && status < 600;
          const extraRetryCodes = [
              // Request Timeout: web server didn't receive full request in time.
              408,
              // Too Many Requests: you're getting rate-limited, basically.
              429
          ];
          const isExtraRetryCode = extraRetryCodes.indexOf(status) !== -1;
          const isRequestSpecificRetryCode = this.additionalRetryCodes_.indexOf(status) !== -1;
          return isFiveHundredCode || isExtraRetryCode || isRequestSpecificRetryCode;
      }
  }
  /**
   * A collection of information about the result of a network request.
   * @param opt_canceled - Defaults to false.
   */
  class RequestEndStatus {
      constructor(wasSuccessCode, xhr, canceled) {
          this.wasSuccessCode = wasSuccessCode;
          this.xhr = xhr;
          this.canceled = !!canceled;
      }
  }
  function addAuthHeader_(headers, authToken) {
      if (authToken !== null && authToken.length > 0) {
          headers['Authorization'] = 'Firebase ' + authToken;
      }
  }
  function addVersionHeader_(headers, firebaseVersion) {
      headers['X-Firebase-Storage-Version'] =
          'webjs/' + (firebaseVersion !== null && firebaseVersion !== void 0 ? firebaseVersion : 'AppManager');
  }
  function addGmpidHeader_(headers, appId) {
      if (appId) {
          headers['X-Firebase-GMPID'] = appId;
      }
  }
  function addAppCheckHeader_(headers, appCheckToken) {
      if (appCheckToken !== null) {
          headers['X-Firebase-AppCheck'] = appCheckToken;
      }
  }
  function makeRequest(requestInfo, appId, authToken, appCheckToken, pool, firebaseVersion) {
      const queryPart = makeQueryString(requestInfo.urlParams);
      const url = requestInfo.url + queryPart;
      const headers = Object.assign({}, requestInfo.headers);
      addGmpidHeader_(headers, appId);
      addAuthHeader_(headers, authToken);
      addVersionHeader_(headers, firebaseVersion);
      addAppCheckHeader_(headers, appCheckToken);
      return new NetworkRequest(url, requestInfo.method, headers, requestInfo.body, requestInfo.successCodes, requestInfo.additionalRetryCodes, requestInfo.handler, requestInfo.errorHandler, requestInfo.timeout, requestInfo.progressCallback, pool);
  }
  
  /**
   * @license
   * Copyright 2017 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 getBlobBuilder() {
      if (typeof BlobBuilder !== 'undefined') {
          return BlobBuilder;
      }
      else if (typeof WebKitBlobBuilder !== 'undefined') {
          return WebKitBlobBuilder;
      }
      else {
          return undefined;
      }
  }
  /**
   * Concatenates one or more values together and converts them to a Blob.
   *
   * @param args The values that will make up the resulting blob.
   * @return The blob.
   */
  function getBlob(...args) {
      const BlobBuilder = getBlobBuilder();
      if (BlobBuilder !== undefined) {
          const bb = new BlobBuilder();
          for (let i = 0; i < args.length; i++) {
              bb.append(args[i]);
          }
          return bb.getBlob();
      }
      else {
          if (isNativeBlobDefined()) {
              return new Blob(args);
          }
          else {
              throw new FirebaseStorageError("unsupported-environment" /* UNSUPPORTED_ENVIRONMENT */, "This browser doesn't seem to support creating Blobs");
          }
      }
  }
  /**
   * Slices the blob. The returned blob contains data from the start byte
   * (inclusive) till the end byte (exclusive). Negative indices cannot be used.
   *
   * @param blob The blob to be sliced.
   * @param start Index of the starting byte.
   * @param end Index of the ending byte.
   * @return The blob slice or null if not supported.
   */
  function sliceBlob(blob, start, end) {
      if (blob.webkitSlice) {
          return blob.webkitSlice(start, end);
      }
      else if (blob.mozSlice) {
          return blob.mozSlice(start, end);
      }
      else if (blob.slice) {
          return blob.slice(start, end);
      }
      return null;
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * An enumeration of the possible string formats for upload.
   * @public
   */
  const StringFormat = {
      /**
       * Indicates the string should be interpreted "raw", that is, as normal text.
       * The string will be interpreted as UTF-16, then uploaded as a UTF-8 byte
       * sequence.
       * Example: The string 'Hello! \\ud83d\\ude0a' becomes the byte sequence
       * 48 65 6c 6c 6f 21 20 f0 9f 98 8a
       */
      RAW: 'raw',
      /**
       * Indicates the string should be interpreted as base64-encoded data.
       * Padding characters (trailing '='s) are optional.
       * Example: The string 'rWmO++E6t7/rlw==' becomes the byte sequence
       * ad 69 8e fb e1 3a b7 bf eb 97
       */
      BASE64: 'base64',
      /**
       * Indicates the string should be interpreted as base64url-encoded data.
       * Padding characters (trailing '='s) are optional.
       * Example: The string 'rWmO--E6t7_rlw==' becomes the byte sequence
       * ad 69 8e fb e1 3a b7 bf eb 97
       */
      BASE64URL: 'base64url',
      /**
       * Indicates the string is a data URL, such as one obtained from
       * canvas.toDataURL().
       * Example: the string 'data:application/octet-stream;base64,aaaa'
       * becomes the byte sequence
       * 69 a6 9a
       * (the content-type "application/octet-stream" is also applied, but can
       * be overridden in the metadata object).
       */
      DATA_URL: 'data_url'
  };
  class StringData {
      constructor(data, contentType) {
          this.data = data;
          this.contentType = contentType || null;
      }
  }
  function dataFromString(format, stringData) {
      switch (format) {
          case StringFormat.RAW:
              return new StringData(utf8Bytes_(stringData));
          case StringFormat.BASE64:
          case StringFormat.BASE64URL:
              return new StringData(base64Bytes_(format, stringData));
          case StringFormat.DATA_URL:
              return new StringData(dataURLBytes_(stringData), dataURLContentType_(stringData));
          // do nothing
      }
      // assert(false);
      throw unknown();
  }
  function utf8Bytes_(value) {
      const b = [];
      for (let i = 0; i < value.length; i++) {
          let c = value.charCodeAt(i);
          if (c <= 127) {
              b.push(c);
          }
          else {
              if (c <= 2047) {
                  b.push(192 | (c >> 6), 128 | (c & 63));
              }
              else {
                  if ((c & 64512) === 55296) {
                      // The start of a surrogate pair.
                      const valid = i < value.length - 1 && (value.charCodeAt(i + 1) & 64512) === 56320;
                      if (!valid) {
                          // The second surrogate wasn't there.
                          b.push(239, 191, 189);
                      }
                      else {
                          const hi = c;
                          const lo = value.charCodeAt(++i);
                          c = 65536 | ((hi & 1023) << 10) | (lo & 1023);
                          b.push(240 | (c >> 18), 128 | ((c >> 12) & 63), 128 | ((c >> 6) & 63), 128 | (c & 63));
                      }
                  }
                  else {
                      if ((c & 64512) === 56320) {
                          // Invalid low surrogate.
                          b.push(239, 191, 189);
                      }
                      else {
                          b.push(224 | (c >> 12), 128 | ((c >> 6) & 63), 128 | (c & 63));
                      }
                  }
              }
          }
      }
      return new Uint8Array(b);
  }
  function percentEncodedBytes_(value) {
      let decoded;
      try {
          decoded = decodeURIComponent(value);
      }
      catch (e) {
          throw invalidFormat(StringFormat.DATA_URL, 'Malformed data URL.');
      }
      return utf8Bytes_(decoded);
  }
  function base64Bytes_(format, value) {
      switch (format) {
          case StringFormat.BASE64: {
              const hasMinus = value.indexOf('-') !== -1;
              const hasUnder = value.indexOf('_') !== -1;
              if (hasMinus || hasUnder) {
                  const invalidChar = hasMinus ? '-' : '_';
                  throw invalidFormat(format, "Invalid character '" +
                      invalidChar +
                      "' found: is it base64url encoded?");
              }
              break;
          }
          case StringFormat.BASE64URL: {
              const hasPlus = value.indexOf('+') !== -1;
              const hasSlash = value.indexOf('/') !== -1;
              if (hasPlus || hasSlash) {
                  const invalidChar = hasPlus ? '+' : '/';
                  throw invalidFormat(format, "Invalid character '" + invalidChar + "' found: is it base64 encoded?");
              }
              value = value.replace(/-/g, '+').replace(/_/g, '/');
              break;
          }
          // do nothing
      }
      let bytes;
      try {
          bytes = atob(value);
      }
      catch (e) {
          throw invalidFormat(format, 'Invalid character found');
      }
      const array = new Uint8Array(bytes.length);
      for (let i = 0; i < bytes.length; i++) {
          array[i] = bytes.charCodeAt(i);
      }
      return array;
  }
  class DataURLParts {
      constructor(dataURL) {
          this.base64 = false;
          this.contentType = null;
          const matches = dataURL.match(/^data:([^,]+)?,/);
          if (matches === null) {
              throw invalidFormat(StringFormat.DATA_URL, "Must be formatted 'data:[<mediatype>][;base64],<data>");
          }
          const middle = matches[1] || null;
          if (middle != null) {
              this.base64 = endsWith(middle, ';base64');
              this.contentType = this.base64
                  ? middle.substring(0, middle.length - ';base64'.length)
                  : middle;
          }
          this.rest = dataURL.substring(dataURL.indexOf(',') + 1);
      }
  }
  function dataURLBytes_(dataUrl) {
      const parts = new DataURLParts(dataUrl);
      if (parts.base64) {
          return base64Bytes_(StringFormat.BASE64, parts.rest);
      }
      else {
          return percentEncodedBytes_(parts.rest);
      }
  }
  function dataURLContentType_(dataUrl) {
      const parts = new DataURLParts(dataUrl);
      return parts.contentType;
  }
  function endsWith(s, end) {
      const longEnough = s.length >= end.length;
      if (!longEnough) {
          return false;
      }
      return s.substring(s.length - end.length) === end;
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * @param opt_elideCopy - If true, doesn't copy mutable input data
   *     (e.g. Uint8Arrays). Pass true only if you know the objects will not be
   *     modified after this blob's construction.
   *
   * @internal
   */
  class FbsBlob {
      constructor(data, elideCopy) {
          let size = 0;
          let blobType = '';
          if (isNativeBlob(data)) {
              this.data_ = data;
              size = data.size;
              blobType = data.type;
          }
          else if (data instanceof ArrayBuffer) {
              if (elideCopy) {
                  this.data_ = new Uint8Array(data);
              }
              else {
                  this.data_ = new Uint8Array(data.byteLength);
                  this.data_.set(new Uint8Array(data));
              }
              size = this.data_.length;
          }
          else if (data instanceof Uint8Array) {
              if (elideCopy) {
                  this.data_ = data;
              }
              else {
                  this.data_ = new Uint8Array(data.length);
                  this.data_.set(data);
              }
              size = data.length;
          }
          this.size_ = size;
          this.type_ = blobType;
      }
      size() {
          return this.size_;
      }
      type() {
          return this.type_;
      }
      slice(startByte, endByte) {
          if (isNativeBlob(this.data_)) {
              const realBlob = this.data_;
              const sliced = sliceBlob(realBlob, startByte, endByte);
              if (sliced === null) {
                  return null;
              }
              return new FbsBlob(sliced);
          }
          else {
              const slice = new Uint8Array(this.data_.buffer, startByte, endByte - startByte);
              return new FbsBlob(slice, true);
          }
      }
      static getBlob(...args) {
          if (isNativeBlobDefined()) {
              const blobby = args.map((val) => {
                  if (val instanceof FbsBlob) {
                      return val.data_;
                  }
                  else {
                      return val;
                  }
              });
              return new FbsBlob(getBlob.apply(null, blobby));
          }
          else {
              const uint8Arrays = args.map((val) => {
                  if (isString(val)) {
                      return dataFromString(StringFormat.RAW, val).data;
                  }
                  else {
                      // Blobs don't exist, so this has to be a Uint8Array.
                      return val.data_;
                  }
              });
              let finalLength = 0;
              uint8Arrays.forEach((array) => {
                  finalLength += array.byteLength;
              });
              const merged = new Uint8Array(finalLength);
              let index = 0;
              uint8Arrays.forEach((array) => {
                  for (let i = 0; i < array.length; i++) {
                      merged[index++] = array[i];
                  }
              });
              return new FbsBlob(merged, true);
          }
      }
      uploadData() {
          return this.data_;
      }
  }
  
  /**
   * @license
   * Copyright 2017 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 the Object resulting from parsing the given JSON, or null if the
   * given string does not represent a JSON object.
   */
  function jsonObjectOrNull(s) {
      let obj;
      try {
          obj = JSON.parse(s);
      }
      catch (e) {
          return null;
      }
      if (isNonArrayObject(obj)) {
          return obj;
      }
      else {
          return null;
      }
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * @fileoverview Contains helper methods for manipulating paths.
   */
  /**
   * @return Null if the path is already at the root.
   */
  function parent(path) {
      if (path.length === 0) {
          return null;
      }
      const index = path.lastIndexOf('/');
      if (index === -1) {
          return '';
      }
      const newPath = path.slice(0, index);
      return newPath;
  }
  function child(path, childPath) {
      const canonicalChildPath = childPath
          .split('/')
          .filter(component => component.length > 0)
          .join('/');
      if (path.length === 0) {
          return canonicalChildPath;
      }
      else {
          return path + '/' + canonicalChildPath;
      }
  }
  /**
   * Returns the last component of a path.
   * '/foo/bar' -> 'bar'
   * '/foo/bar/baz/' -> 'baz/'
   * '/a' -> 'a'
   */
  function lastComponent(path) {
      const index = path.lastIndexOf('/', path.length - 2);
      if (index === -1) {
          return path;
      }
      else {
          return path.slice(index + 1);
      }
  }
  
  /**
   * @license
   * Copyright 2017 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 noXform_(metadata, value) {
      return value;
  }
  class Mapping {
      constructor(server, local, writable, xform) {
          this.server = server;
          this.local = local || server;
          this.writable = !!writable;
          this.xform = xform || noXform_;
      }
  }
  let mappings_ = null;
  function xformPath(fullPath) {
      if (!isString(fullPath) || fullPath.length < 2) {
          return fullPath;
      }
      else {
          return lastComponent(fullPath);
      }
  }
  function getMappings() {
      if (mappings_) {
          return mappings_;
      }
      const mappings = [];
      mappings.push(new Mapping('bucket'));
      mappings.push(new Mapping('generation'));
      mappings.push(new Mapping('metageneration'));
      mappings.push(new Mapping('name', 'fullPath', true));
      function mappingsXformPath(_metadata, fullPath) {
          return xformPath(fullPath);
      }
      const nameMapping = new Mapping('name');
      nameMapping.xform = mappingsXformPath;
      mappings.push(nameMapping);
      /**
       * Coerces the second param to a number, if it is defined.
       */
      function xformSize(_metadata, size) {
          if (size !== undefined) {
              return Number(size);
          }
          else {
              return size;
          }
      }
      const sizeMapping = new Mapping('size');
      sizeMapping.xform = xformSize;
      mappings.push(sizeMapping);
      mappings.push(new Mapping('timeCreated'));
      mappings.push(new Mapping('updated'));
      mappings.push(new Mapping('md5Hash', null, true));
      mappings.push(new Mapping('cacheControl', null, true));
      mappings.push(new Mapping('contentDisposition', null, true));
      mappings.push(new Mapping('contentEncoding', null, true));
      mappings.push(new Mapping('contentLanguage', null, true));
      mappings.push(new Mapping('contentType', null, true));
      mappings.push(new Mapping('metadata', 'customMetadata', true));
      mappings_ = mappings;
      return mappings_;
  }
  function addRef(metadata, service) {
      function generateRef() {
          const bucket = metadata['bucket'];
          const path = metadata['fullPath'];
          const loc = new Location(bucket, path);
          return service._makeStorageReference(loc);
      }
      Object.defineProperty(metadata, 'ref', { get: generateRef });
  }
  function fromResource(service, resource, mappings) {
      const metadata = {};
      metadata['type'] = 'file';
      const len = mappings.length;
      for (let i = 0; i < len; i++) {
          const mapping = mappings[i];
          metadata[mapping.local] = mapping.xform(metadata, resource[mapping.server]);
      }
      addRef(metadata, service);
      return metadata;
  }
  function fromResourceString(service, resourceString, mappings) {
      const obj = jsonObjectOrNull(resourceString);
      if (obj === null) {
          return null;
      }
      const resource = obj;
      return fromResource(service, resource, mappings);
  }
  function downloadUrlFromResourceString(metadata, resourceString, host) {
      const obj = jsonObjectOrNull(resourceString);
      if (obj === null) {
          return null;
      }
      if (!isString(obj['downloadTokens'])) {
          // This can happen if objects are uploaded through GCS and retrieved
          // through list, so we don't want to throw an Error.
          return null;
      }
      const tokens = obj['downloadTokens'];
      if (tokens.length === 0) {
          return null;
      }
      const encode = encodeURIComponent;
      const tokensList = tokens.split(',');
      const urls = tokensList.map((token) => {
          const bucket = metadata['bucket'];
          const path = metadata['fullPath'];
          const urlPart = '/b/' + encode(bucket) + '/o/' + encode(path);
          const base = makeUrl(urlPart, host);
          const queryString = makeQueryString({
              alt: 'media',
              token
          });
          return base + queryString;
      });
      return urls[0];
  }
  function toResourceString(metadata, mappings) {
      const resource = {};
      const len = mappings.length;
      for (let i = 0; i < len; i++) {
          const mapping = mappings[i];
          if (mapping.writable) {
              resource[mapping.server] = metadata[mapping.local];
          }
      }
      return JSON.stringify(resource);
  }
  
  /**
   * @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 PREFIXES_KEY = 'prefixes';
  const ITEMS_KEY = 'items';
  function fromBackendResponse(service, bucket, resource) {
      const listResult = {
          prefixes: [],
          items: [],
          nextPageToken: resource['nextPageToken']
      };
      if (resource[PREFIXES_KEY]) {
          for (const path of resource[PREFIXES_KEY]) {
              const pathWithoutTrailingSlash = path.replace(/\/$/, '');
              const reference = service._makeStorageReference(new Location(bucket, pathWithoutTrailingSlash));
              listResult.prefixes.push(reference);
          }
      }
      if (resource[ITEMS_KEY]) {
          for (const item of resource[ITEMS_KEY]) {
              const reference = service._makeStorageReference(new Location(bucket, item['name']));
              listResult.items.push(reference);
          }
      }
      return listResult;
  }
  function fromResponseString(service, bucket, resourceString) {
      const obj = jsonObjectOrNull(resourceString);
      if (obj === null) {
          return null;
      }
      const resource = obj;
      return fromBackendResponse(service, bucket, resource);
  }
  
  class RequestInfo {
      constructor(url, method, 
      /**
       * Returns the value with which to resolve the request's promise. Only called
       * if the request is successful. Throw from this function to reject the
       * returned Request's promise with the thrown error.
       * Note: The XhrIo passed to this function may be reused after this callback
       * returns. Do not keep a reference to it in any way.
       */
      handler, timeout) {
          this.url = url;
          this.method = method;
          this.handler = handler;
          this.timeout = timeout;
          this.urlParams = {};
          this.headers = {};
          this.body = null;
          this.errorHandler = null;
          /**
           * Called with the current number of bytes uploaded and total size (-1 if not
           * computable) of the request body (i.e. used to report upload progress).
           */
          this.progressCallback = null;
          this.successCodes = [200];
          this.additionalRetryCodes = [];
      }
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * Throws the UNKNOWN FirebaseStorageError if cndn is false.
   */
  function handlerCheck(cndn) {
      if (!cndn) {
          throw unknown();
      }
  }
  function metadataHandler(service, mappings) {
      function handler(xhr, text) {
          const metadata = fromResourceString(service, text, mappings);
          handlerCheck(metadata !== null);
          return metadata;
      }
      return handler;
  }
  function listHandler(service, bucket) {
      function handler(xhr, text) {
          const listResult = fromResponseString(service, bucket, text);
          handlerCheck(listResult !== null);
          return listResult;
      }
      return handler;
  }
  function downloadUrlHandler(service, mappings) {
      function handler(xhr, text) {
          const metadata = fromResourceString(service, text, mappings);
          handlerCheck(metadata !== null);
          return downloadUrlFromResourceString(metadata, text, service.host);
      }
      return handler;
  }
  function sharedErrorHandler(location) {
      function errorHandler(xhr, err) {
          let newErr;
          if (xhr.getStatus() === 401) {
              if (
              // This exact message string is the only consistent part of the
              // server's error response that identifies it as an App Check error.
              xhr.getResponseText().includes('Firebase App Check token is invalid')) {
                  newErr = unauthorizedApp();
              }
              else {
                  newErr = unauthenticated();
              }
          }
          else {
              if (xhr.getStatus() === 402) {
                  newErr = quotaExceeded(location.bucket);
              }
              else {
                  if (xhr.getStatus() === 403) {
                      newErr = unauthorized(location.path);
                  }
                  else {
                      newErr = err;
                  }
              }
          }
          newErr.serverResponse = err.serverResponse;
          return newErr;
      }
      return errorHandler;
  }
  function objectErrorHandler(location) {
      const shared = sharedErrorHandler(location);
      function errorHandler(xhr, err) {
          let newErr = shared(xhr, err);
          if (xhr.getStatus() === 404) {
              newErr = objectNotFound(location.path);
          }
          newErr.serverResponse = err.serverResponse;
          return newErr;
      }
      return errorHandler;
  }
  function getMetadata(service, location, mappings) {
      const urlPart = location.fullServerUrl();
      const url = makeUrl(urlPart, service.host);
      const method = 'GET';
      const timeout = service.maxOperationRetryTime;
      const requestInfo = new RequestInfo(url, method, metadataHandler(service, mappings), timeout);
      requestInfo.errorHandler = objectErrorHandler(location);
      return requestInfo;
  }
  function list(service, location, delimiter, pageToken, maxResults) {
      const urlParams = {};
      if (location.isRoot) {
          urlParams['prefix'] = '';
      }
      else {
          urlParams['prefix'] = location.path + '/';
      }
      if (delimiter && delimiter.length > 0) {
          urlParams['delimiter'] = delimiter;
      }
      if (pageToken) {
          urlParams['pageToken'] = pageToken;
      }
      if (maxResults) {
          urlParams['maxResults'] = maxResults;
      }
      const urlPart = location.bucketOnlyServerUrl();
      const url = makeUrl(urlPart, service.host);
      const method = 'GET';
      const timeout = service.maxOperationRetryTime;
      const requestInfo = new RequestInfo(url, method, listHandler(service, location.bucket), timeout);
      requestInfo.urlParams = urlParams;
      requestInfo.errorHandler = sharedErrorHandler(location);
      return requestInfo;
  }
  function getDownloadUrl(service, location, mappings) {
      const urlPart = location.fullServerUrl();
      const url = makeUrl(urlPart, service.host);
      const method = 'GET';
      const timeout = service.maxOperationRetryTime;
      const requestInfo = new RequestInfo(url, method, downloadUrlHandler(service, mappings), timeout);
      requestInfo.errorHandler = objectErrorHandler(location);
      return requestInfo;
  }
  function updateMetadata(service, location, metadata, mappings) {
      const urlPart = location.fullServerUrl();
      const url = makeUrl(urlPart, service.host);
      const method = 'PATCH';
      const body = toResourceString(metadata, mappings);
      const headers = { 'Content-Type': 'application/json; charset=utf-8' };
      const timeout = service.maxOperationRetryTime;
      const requestInfo = new RequestInfo(url, method, metadataHandler(service, mappings), timeout);
      requestInfo.headers = headers;
      requestInfo.body = body;
      requestInfo.errorHandler = objectErrorHandler(location);
      return requestInfo;
  }
  function deleteObject(service, location) {
      const urlPart = location.fullServerUrl();
      const url = makeUrl(urlPart, service.host);
      const method = 'DELETE';
      const timeout = service.maxOperationRetryTime;
      function handler(_xhr, _text) { }
      const requestInfo = new RequestInfo(url, method, handler, timeout);
      requestInfo.successCodes = [200, 204];
      requestInfo.errorHandler = objectErrorHandler(location);
      return requestInfo;
  }
  function determineContentType_(metadata, blob) {
      return ((metadata && metadata['contentType']) ||
          (blob && blob.type()) ||
          'application/octet-stream');
  }
  function metadataForUpload_(location, blob, metadata) {
      const metadataClone = Object.assign({}, metadata);
      metadataClone['fullPath'] = location.path;
      metadataClone['size'] = blob.size();
      if (!metadataClone['contentType']) {
          metadataClone['contentType'] = determineContentType_(null, blob);
      }
      return metadataClone;
  }
  /**
   * Prepare RequestInfo for uploads as Content-Type: multipart.
   */
  function multipartUpload(service, location, mappings, blob, metadata) {
      const urlPart = location.bucketOnlyServerUrl();
      const headers = {
          'X-Goog-Upload-Protocol': 'multipart'
      };
      function genBoundary() {
          let str = '';
          for (let i = 0; i < 2; i++) {
              str = str + Math.random().toString().slice(2);
          }
          return str;
      }
      const boundary = genBoundary();
      headers['Content-Type'] = 'multipart/related; boundary=' + boundary;
      const metadata_ = metadataForUpload_(location, blob, metadata);
      const metadataString = toResourceString(metadata_, mappings);
      const preBlobPart = '--' +
          boundary +
          '\r\n' +
          'Content-Type: application/json; charset=utf-8\r\n\r\n' +
          metadataString +
          '\r\n--' +
          boundary +
          '\r\n' +
          'Content-Type: ' +
          metadata_['contentType'] +
          '\r\n\r\n';
      const postBlobPart = '\r\n--' + boundary + '--';
      const body = FbsBlob.getBlob(preBlobPart, blob, postBlobPart);
      if (body === null) {
          throw cannotSliceBlob();
      }
      const urlParams = { name: metadata_['fullPath'] };
      const url = makeUrl(urlPart, service.host);
      const method = 'POST';
      const timeout = service.maxUploadRetryTime;
      const requestInfo = new RequestInfo(url, method, metadataHandler(service, mappings), timeout);
      requestInfo.urlParams = urlParams;
      requestInfo.headers = headers;
      requestInfo.body = body.uploadData();
      requestInfo.errorHandler = sharedErrorHandler(location);
      return requestInfo;
  }
  /**
   * @param current The number of bytes that have been uploaded so far.
   * @param total The total number of bytes in the upload.
   * @param opt_finalized True if the server has finished the upload.
   * @param opt_metadata The upload metadata, should
   *     only be passed if opt_finalized is true.
   */
  class ResumableUploadStatus {
      constructor(current, total, finalized, metadata) {
          this.current = current;
          this.total = total;
          this.finalized = !!finalized;
          this.metadata = metadata || null;
      }
  }
  function checkResumeHeader_(xhr, allowed) {
      let status = null;
      try {
          status = xhr.getResponseHeader('X-Goog-Upload-Status');
      }
      catch (e) {
          handlerCheck(false);
      }
      const allowedStatus = allowed || ['active'];
      handlerCheck(!!status && allowedStatus.indexOf(status) !== -1);
      return status;
  }
  function createResumableUpload(service, location, mappings, blob, metadata) {
      const urlPart = location.bucketOnlyServerUrl();
      const metadataForUpload = metadataForUpload_(location, blob, metadata);
      const urlParams = { name: metadataForUpload['fullPath'] };
      const url = makeUrl(urlPart, service.host);
      const method = 'POST';
      const headers = {
          'X-Goog-Upload-Protocol': 'resumable',
          'X-Goog-Upload-Command': 'start',
          'X-Goog-Upload-Header-Content-Length': blob.size(),
          'X-Goog-Upload-Header-Content-Type': metadataForUpload['contentType'],
          'Content-Type': 'application/json; charset=utf-8'
      };
      const body = toResourceString(metadataForUpload, mappings);
      const timeout = service.maxUploadRetryTime;
      function handler(xhr) {
          checkResumeHeader_(xhr);
          let url;
          try {
              url = xhr.getResponseHeader('X-Goog-Upload-URL');
          }
          catch (e) {
              handlerCheck(false);
          }
          handlerCheck(isString(url));
          return url;
      }
      const requestInfo = new RequestInfo(url, method, handler, timeout);
      requestInfo.urlParams = urlParams;
      requestInfo.headers = headers;
      requestInfo.body = body;
      requestInfo.errorHandler = sharedErrorHandler(location);
      return requestInfo;
  }
  /**
   * @param url From a call to fbs.requests.createResumableUpload.
   */
  function getResumableUploadStatus(service, location, url, blob) {
      const headers = { 'X-Goog-Upload-Command': 'query' };
      function handler(xhr) {
          const status = checkResumeHeader_(xhr, ['active', 'final']);
          let sizeString = null;
          try {
              sizeString = xhr.getResponseHeader('X-Goog-Upload-Size-Received');
          }
          catch (e) {
              handlerCheck(false);
          }
          if (!sizeString) {
              // null or empty string
              handlerCheck(false);
          }
          const size = Number(sizeString);
          handlerCheck(!isNaN(size));
          return new ResumableUploadStatus(size, blob.size(), status === 'final');
      }
      const method = 'POST';
      const timeout = service.maxUploadRetryTime;
      const requestInfo = new RequestInfo(url, method, handler, timeout);
      requestInfo.headers = headers;
      requestInfo.errorHandler = sharedErrorHandler(location);
      return requestInfo;
  }
  /**
   * Any uploads via the resumable upload API must transfer a number of bytes
   * that is a multiple of this number.
   */
  const RESUMABLE_UPLOAD_CHUNK_SIZE = 256 * 1024;
  /**
   * @param url From a call to fbs.requests.createResumableUpload.
   * @param chunkSize Number of bytes to upload.
   * @param status The previous status.
   *     If not passed or null, we start from the beginning.
   * @throws fbs.Error If the upload is already complete, the passed in status
   *     has a final size inconsistent with the blob, or the blob cannot be sliced
   *     for upload.
   */
  function continueResumableUpload(location, service, url, blob, chunkSize, mappings, status, progressCallback) {
      // TODO(andysoto): standardize on internal asserts
      // assert(!(opt_status && opt_status.finalized));
      const status_ = new ResumableUploadStatus(0, 0);
      if (status) {
          status_.current = status.current;
          status_.total = status.total;
      }
      else {
          status_.current = 0;
          status_.total = blob.size();
      }
      if (blob.size() !== status_.total) {
          throw serverFileWrongSize();
      }
      const bytesLeft = status_.total - status_.current;
      let bytesToUpload = bytesLeft;
      if (chunkSize > 0) {
          bytesToUpload = Math.min(bytesToUpload, chunkSize);
      }
      const startByte = status_.current;
      const endByte = startByte + bytesToUpload;
      const uploadCommand = bytesToUpload === bytesLeft ? 'upload, finalize' : 'upload';
      const headers = {
          'X-Goog-Upload-Command': uploadCommand,
          'X-Goog-Upload-Offset': status_.current
      };
      const body = blob.slice(startByte, endByte);
      if (body === null) {
          throw cannotSliceBlob();
      }
      function handler(xhr, text) {
          // TODO(andysoto): Verify the MD5 of each uploaded range:
          // the 'x-range-md5' header comes back with status code 308 responses.
          // We'll only be able to bail out though, because you can't re-upload a
          // range that you previously uploaded.
          const uploadStatus = checkResumeHeader_(xhr, ['active', 'final']);
          const newCurrent = status_.current + bytesToUpload;
          const size = blob.size();
          let metadata;
          if (uploadStatus === 'final') {
              metadata = metadataHandler(service, mappings)(xhr, text);
          }
          else {
              metadata = null;
          }
          return new ResumableUploadStatus(newCurrent, size, uploadStatus === 'final', metadata);
      }
      const method = 'POST';
      const timeout = service.maxUploadRetryTime;
      const requestInfo = new RequestInfo(url, method, handler, timeout);
      requestInfo.headers = headers;
      requestInfo.body = body.uploadData();
      requestInfo.progressCallback = progressCallback || null;
      requestInfo.errorHandler = sharedErrorHandler(location);
      return requestInfo;
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * Represents the current state of a running upload.
   */
  const TaskState = {
      /** The task is currently transferring data. */
      RUNNING: 'running',
      /** The task was paused by the user. */
      PAUSED: 'paused',
      /** The task completed successfully. */
      SUCCESS: 'success',
      /** The task was canceled. */
      CANCELED: 'canceled',
      /** The task failed with an error. */
      ERROR: 'error'
  };
  function taskStateFromInternalTaskState(state) {
      switch (state) {
          case "running" /* RUNNING */:
          case "pausing" /* PAUSING */:
          case "canceling" /* CANCELING */:
              return TaskState.RUNNING;
          case "paused" /* PAUSED */:
              return TaskState.PAUSED;
          case "success" /* SUCCESS */:
              return TaskState.SUCCESS;
          case "canceled" /* CANCELED */:
              return TaskState.CANCELED;
          case "error" /* ERROR */:
              return TaskState.ERROR;
          default:
              // TODO(andysoto): assert(false);
              return TaskState.ERROR;
      }
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  class Observer {
      constructor(nextOrObserver, error, complete) {
          const asFunctions = isFunction(nextOrObserver) || error != null || complete != null;
          if (asFunctions) {
              this.next = nextOrObserver;
              this.error = error;
              this.complete = complete;
          }
          else {
              const observer = nextOrObserver;
              this.next = observer.next;
              this.error = observer.error;
              this.complete = observer.complete;
          }
      }
  }
  
  /**
   * @license
   * Copyright 2017 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 function that invokes f with its arguments asynchronously as a
   * microtask, i.e. as soon as possible after the current script returns back
   * into browser code.
   */
  // eslint-disable-next-line @typescript-eslint/ban-types
  function async(f) {
      return (...argsToForward) => {
          // eslint-disable-next-line @typescript-eslint/no-floating-promises
          Promise.resolve().then(() => f(...argsToForward));
      };
  }
  
  /**
   * @license
   * Copyright 2017 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.
   */
  /**
   * Represents a blob being uploaded. Can be used to pause/resume/cancel the
   * upload and manage callbacks for various events.
   * @internal
   */
  class UploadTask {
      /**
       * @param ref - The firebaseStorage.Reference object this task came
       *     from, untyped to avoid cyclic dependencies.
       * @param blob - The blob to upload.
       */
      constructor(ref, blob, metadata = null) {
          /**
           * Number of bytes transferred so far.
           */
          this._transferred = 0;
          this._needToFetchStatus = false;
          this._needToFetchMetadata = false;
          this._observers = [];
          this._error = undefined;
          this._uploadUrl = undefined;
          this._request = undefined;
          this._chunkMultiplier = 1;
          this._resolve = undefined;
          this._reject = undefined;
          this._ref = ref;
          this._blob = blob;
          this._metadata = metadata;
          this._mappings = getMappings();
          this._resumable = this._shouldDoResumable(this._blob);
          this._state = "running" /* RUNNING */;
          this._errorHandler = error => {
              this._request = undefined;
              this._chunkMultiplier = 1;
              if (error._codeEquals("canceled" /* CANCELED */)) {
                  this._needToFetchStatus = true;
                  this.completeTransitions_();
              }
              else {
                  this._error = error;
                  this._transition("error" /* ERROR */);
              }
          };
          this._metadataErrorHandler = error => {
              this._request = undefined;
              if (error._codeEquals("canceled" /* CANCELED */)) {
                  this.completeTransitions_();
              }
              else {
                  this._error = error;
                  this._transition("error" /* ERROR */);
              }
          };
          this._promise = new Promise((resolve, reject) => {
              this._resolve = resolve;
              this._reject = reject;
              this._start();
          });
          // Prevent uncaught rejections on the internal promise from bubbling out
          // to the top level with a dummy handler.
          this._promise.then(null, () => { });
      }
      _makeProgressCallback() {
          const sizeBefore = this._transferred;
          return loaded => this._updateProgress(sizeBefore + loaded);
      }
      _shouldDoResumable(blob) {
          return blob.size() > 256 * 1024;
      }
      _start() {
          if (this._state !== "running" /* RUNNING */) {
              // This can happen if someone pauses us in a resume callback, for example.
              return;
          }
          if (this._request !== undefined) {
              return;
          }
          if (this._resumable) {
              if (this._uploadUrl === undefined) {
                  this._createResumable();
              }
              else {
                  if (this._needToFetchStatus) {
                      this._fetchStatus();
                  }
                  else {
                      if (this._needToFetchMetadata) {
                          // Happens if we miss the metadata on upload completion.
                          this._fetchMetadata();
                      }
                      else {
                          this._continueUpload();
                      }
                  }
              }
          }
          else {
              this._oneShotUpload();
          }
      }
      _resolveToken(callback) {
          // eslint-disable-next-line @typescript-eslint/no-floating-promises
          Promise.all([
              this._ref.storage._getAuthToken(),
              this._ref.storage._getAppCheckToken()
          ]).then(([authToken, appCheckToken]) => {
              switch (this._state) {
                  case "running" /* RUNNING */:
                      callback(authToken, appCheckToken);
                      break;
                  case "canceling" /* CANCELING */:
                      this._transition("canceled" /* CANCELED */);
                      break;
                  case "pausing" /* PAUSING */:
                      this._transition("paused" /* PAUSED */);
                      break;
              }
          });
      }
      // TODO(andysoto): assert false
      _createResumable() {
          this._resolveToken((authToken, appCheckToken) => {
              const requestInfo = createResumableUpload(this._ref.storage, this._ref._location, this._mappings, this._blob, this._metadata);
              const createRequest = this._ref.storage._makeRequest(requestInfo, authToken, appCheckToken);
              this._request = createRequest;
              createRequest.getPromise().then((url) => {
                  this._request = undefined;
                  this._uploadUrl = url;
                  this._needToFetchStatus = false;
                  this.completeTransitions_();
              }, this._errorHandler);
          });
      }
      _fetchStatus() {
          // TODO(andysoto): assert(this.uploadUrl_ !== null);
          const url = this._uploadUrl;
          this._resolveToken((authToken, appCheckToken) => {
              const requestInfo = getResumableUploadStatus(this._ref.storage, this._ref._location, url, this._blob);
              const statusRequest = this._ref.storage._makeRequest(requestInfo, authToken, appCheckToken);
              this._request = statusRequest;
              statusRequest.getPromise().then(status => {
                  status = status;
                  this._request = undefined;
                  this._updateProgress(status.current);
                  this._needToFetchStatus = false;
                  if (status.finalized) {
                      this._needToFetchMetadata = true;
                  }
                  this.completeTransitions_();
              }, this._errorHandler);
          });
      }
      _continueUpload() {
          const chunkSize = RESUMABLE_UPLOAD_CHUNK_SIZE * this._chunkMultiplier;
          const status = new ResumableUploadStatus(this._transferred, this._blob.size());
          // TODO(andysoto): assert(this.uploadUrl_ !== null);
          const url = this._uploadUrl;
          this._resolveToken((authToken, appCheckToken) => {
              let requestInfo;
              try {
                  requestInfo = continueResumableUpload(this._ref._location, this._ref.storage, url, this._blob, chunkSize, this._mappings, status, this._makeProgressCallback());
              }
              catch (e) {
                  this._error = e;
                  this._transition("error" /* ERROR */);
                  return;
              }
              const uploadRequest = this._ref.storage._makeRequest(requestInfo, authToken, appCheckToken);
              this._request = uploadRequest;
              uploadRequest.getPromise().then((newStatus) => {
                  this._increaseMultiplier();
                  this._request = undefined;
                  this._updateProgress(newStatus.current);
                  if (newStatus.finalized) {
                      this._metadata = newStatus.metadata;
                      this._transition("success" /* SUCCESS */);
                  }
                  else {
                      this.completeTransitions_();
                  }
              }, this._errorHandler);
          });
      }
      _increaseMultiplier() {
          const currentSize = RESUMABLE_UPLOAD_CHUNK_SIZE * this._chunkMultiplier;
          // Max chunk size is 32M.
          if (currentSize < 32 * 1024 * 1024) {
              this._chunkMultiplier *= 2;
          }
      }
      _fetchMetadata() {
          this._resolveToken((authToken, appCheckToken) => {
              const requestInfo = getMetadata(this._ref.storage, this._ref._location, this._mappings);
              const metadataRequest = this._ref.storage._makeRequest(requestInfo, authToken, appCheckToken);
              this._request = metadataRequest;
              metadataRequest.getPromise().then(metadata => {
                  this._request = undefined;
                  this._metadata = metadata;
                  this._transition("success" /* SUCCESS */);
              }, this._metadataErrorHandler);
          });
      }
      _oneShotUpload() {
          this._resolveToken((authToken, appCheckToken) => {
              const requestInfo = multipartUpload(this._ref.storage, this._ref._location, this._mappings, this._blob, this._metadata);
              const multipartRequest = this._ref.storage._makeRequest(requestInfo, authToken, appCheckToken);
              this._request = multipartRequest;
              multipartRequest.getPromise().then(metadata => {
                  this._request = undefined;
                  this._metadata = metadata;
                  this._updateProgress(this._blob.size());
                  this._transition("success" /* SUCCESS */);
              }, this._errorHandler);
          });
      }
      _updateProgress(transferred) {
          const old = this._transferred;
          this._transferred = transferred;
          // A progress update can make the "transferred" value smaller (e.g. a
          // partial upload not completed by server, after which the "transferred"
          // value may reset to the value at the beginning of the request).
          if (this._transferred !== old) {
              this._notifyObservers();
          }
      }
      _transition(state) {
          if (this._state === state) {
              return;
          }
          switch (state) {
              case "canceling" /* CANCELING */:
                  // TODO(andysoto):
                  // assert(this.state_ === InternalTaskState.RUNNING ||
                  //        this.state_ === InternalTaskState.PAUSING);
                  this._state = state;
                  if (this._request !== undefined) {
                      this._request.cancel();
                  }
                  break;
              case "pausing" /* PAUSING */:
                  // TODO(andysoto):
                  // assert(this.state_ === InternalTaskState.RUNNING);
                  this._state = state;
                  if (this._request !== undefined) {
                      this._request.cancel();
                  }
                  break;
              case "running" /* RUNNING */:
                  // TODO(andysoto):
                  // assert(this.state_ === InternalTaskState.PAUSED ||
                  //        this.state_ === InternalTaskState.PAUSING);
                  const wasPaused = this._state === "paused" /* PAUSED */;
                  this._state = state;
                  if (wasPaused) {
                      this._notifyObservers();
                      this._start();
                  }
                  break;
              case "paused" /* PAUSED */:
                  // TODO(andysoto):
                  // assert(this.state_ === InternalTaskState.PAUSING);
                  this._state = state;
                  this._notifyObservers();
                  break;
              case "canceled" /* CANCELED */:
                  // TODO(andysoto):
                  // assert(this.state_ === InternalTaskState.PAUSED ||
                  //        this.state_ === InternalTaskState.CANCELING);
                  this._error = canceled();
                  this._state = state;
                  this._notifyObservers();
                  break;
              case "error" /* ERROR */:
                  // TODO(andysoto):
                  // assert(this.state_ === InternalTaskState.RUNNING ||
                  //        this.state_ === InternalTaskState.PAUSING ||
                  //        this.state_ === InternalTaskState.CANCELING);
                  this._state = state;
                  this._notifyObservers();
                  break;
              case "success" /* SUCCESS */:
                  // TODO(andysoto):
                  // assert(this.state_ === InternalTaskState.RUNNING ||
                  //        this.state_ === InternalTaskState.PAUSING ||
                  //        this.state_ === InternalTaskState.CANCELING);
                  this._state = state;
                  this._notifyObservers();
                  break;
          }
      }
      completeTransitions_() {
          switch (this._state) {
              case "pausing" /* PAUSING */:
                  this._transition("paused" /* PAUSED */);
                  break;
              case "canceling" /* CANCELING */:
                  this._transition("canceled" /* CANCELED */);
                  break;
              case "running" /* RUNNING */:
                  this._start();
                  break;
          }
      }
      /**
       * A snapshot of the current task state.
       */
      get snapshot() {
          const externalState = taskStateFromInternalTaskState(this._state);
          return {
              bytesTransferred: this._transferred,
              totalBytes: this._blob.size(),
              state: externalState,
              metadata: this._metadata,
              task: this,
              ref: this._ref
          };
      }
      /**
       * Adds a callback for an event.
       * @param type - The type of event to listen for.
       * @param nextOrObserver -
       *     The `next` function, which gets called for each item in
       *     the event stream, or an observer object with some or all of these three
       *     properties (`next`, `error`, `complete`).
       * @param error - A function that gets called with a `FirebaseStorageError`
       *     if the event stream ends due to an error.
       * @param completed - A function that gets called if the
       *     event stream ends normally.
       * @returns
       *     If only the event argument is passed, returns a function you can use to
       *     add callbacks (see the examples above). If more than just the event
       *     argument is passed, returns a function you can call to unregister the
       *     callbacks.
       */
      on(type, nextOrObserver, error, completed) {
          const observer = new Observer(nextOrObserver, error, completed);
          this._addObserver(observer);
          return () => {
              this._removeObserver(observer);
          };
      }
      /**
       * This object behaves like a Promise, and resolves with its snapshot data
       * when the upload completes.
       * @param onFulfilled - The fulfillment callback. Promise chaining works as normal.
       * @param onRejected - The rejection callback.
       */
      then(onFulfilled, onRejected) {
          // These casts are needed so that TypeScript can infer the types of the
          // resulting Promise.
          return this._promise.then(onFulfilled, onRejected);
      }
      /**
       * Equivalent to calling `then(null, onRejected)`.
       */
      catch(onRejected) {
          return this.then(null, onRejected);
      }
      /**
       * Adds the given observer.
       */
      _addObserver(observer) {
          this._observers.push(observer);
          this._notifyObserver(observer);
      }
      /**
       * Removes the given observer.
       */
      _removeObserver(observer) {
          const i = this._observers.indexOf(observer);
          if (i !== -1) {
              this._observers.splice(i, 1);
          }
      }
      _notifyObservers() {
          this._finishPromise();
          const observers = this._observers.slice();
          observers.forEach(observer => {
              this._notifyObserver(observer);
          });
      }
      _finishPromise() {
          if (this._resolve !== undefined) {
              let triggered = true;
              switch (taskStateFromInternalTaskState(this._state)) {
                  case TaskState.SUCCESS:
                      async(this._resolve.bind(null, this.snapshot))();
                      break;
                  case TaskState.CANCELED:
                  case TaskState.ERROR:
                      const toCall = this._reject;
                      async(toCall.bind(null, this._error))();
                      break;
                  default:
                      triggered = false;
                      break;
              }
              if (triggered) {
                  this._resolve = undefined;
                  this._reject = undefined;
              }
          }
      }
      _notifyObserver(observer) {
          const externalState = taskStateFromInternalTaskState(this._state);
          switch (externalState) {
              case TaskState.RUNNING:
              case TaskState.PAUSED:
                  if (observer.next) {
                      async(observer.next.bind(observer, this.snapshot))();
                  }
                  break;
              case TaskState.SUCCESS:
                  if (observer.complete) {
                      async(observer.complete.bind(observer))();
                  }
                  break;
              case TaskState.CANCELED:
              case TaskState.ERROR:
                  if (observer.error) {
                      async(observer.error.bind(observer, this._error))();
                  }
                  break;
              default:
                  // TODO(andysoto): assert(false);
                  if (observer.error) {
                      async(observer.error.bind(observer, this._error))();
                  }
          }
      }
      /**
       * Resumes a paused task. Has no effect on a currently running or failed task.
       * @returns True if the operation took effect, false if ignored.
       */
      resume() {
          const valid = this._state === "paused" /* PAUSED */ ||
              this._state === "pausing" /* PAUSING */;
          if (valid) {
              this._transition("running" /* RUNNING */);
          }
          return valid;
      }
      /**
       * Pauses a currently running task. Has no effect on a paused or failed task.
       * @returns True if the operation took effect, false if ignored.
       */
      pause() {
          const valid = this._state === "running" /* RUNNING */;
          if (valid) {
              this._transition("pausing" /* PAUSING */);
          }
          return valid;
      }
      /**
       * Cancels a currently running or paused task. Has no effect on a complete or
       * failed task.
       * @returns True if the operation took effect, false if ignored.
       */
      cancel() {
          const valid = this._state === "running" /* RUNNING */ ||
              this._state === "pausing" /* PAUSING */;
          if (valid) {
              this._transition("canceling" /* CANCELING */);
          }
          return valid;
      }
  }
  
  /**
   * @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.
   */
  /**
   * Provides methods to interact with a bucket in the Firebase Storage service.
   * @internal
   * @param _location - An fbs.location, or the URL at
   *     which to base this object, in one of the following forms:
   *         gs://<bucket>/<object-path>
   *         http[s]://firebasestorage.googleapis.com/
   *                     <api-version>/b/<bucket>/o/<object-path>
   *     Any query or fragment strings will be ignored in the http[s]
   *     format. If no value is passed, the storage object will use a URL based on
   *     the project ID of the base firebase.App instance.
   */
  class Reference {
      constructor(_service, location) {
          this._service = _service;
          if (location instanceof Location) {
              this._location = location;
          }
          else {
              this._location = Location.makeFromUrl(location, _service.host);
          }
      }
      /**
       * Returns the URL for the bucket and path this object references,
       *     in the form gs://<bucket>/<object-path>
       * @override
       */
      toString() {
          return 'gs://' + this._location.bucket + '/' + this._location.path;
      }
      _newRef(service, location) {
          return new Reference(service, location);
      }
      /**
       * A reference to the root of this object's bucket.
       */
      get root() {
          const location = new Location(this._location.bucket, '');
          return this._newRef(this._service, location);
      }
      /**
       * The name of the bucket containing this reference's object.
       */
      get bucket() {
          return this._location.bucket;
      }
      /**
       * The full path of this object.
       */
      get fullPath() {
          return this._location.path;
      }
      /**
       * The short name of this object, which is the last component of the full path.
       * For example, if fullPath is 'full/path/image.png', name is 'image.png'.
       */
      get name() {
          return lastComponent(this._location.path);
      }
      /**
       * The `StorageService` instance this `StorageReference` is associated with.
       */
      get storage() {
          return this._service;
      }
      /**
       * A `StorageReference` pointing to the parent location of this `StorageReference`, or null if
       * this reference is the root.
       */
      get parent() {
          const newPath = parent(this._location.path);
          if (newPath === null) {
              return null;
          }
          const location = new Location(this._location.bucket, newPath);
          return new Reference(this._service, location);
      }
      /**
       * Utility function to throw an error in methods that do not accept a root reference.
       */
      _throwIfRoot(name) {
          if (this._location.path === '') {
              throw invalidRootOperation(name);
          }
      }
  }
  /**
   * Uploads data to this object's location.
   * The upload is not resumable.
   *
   * @param ref - StorageReference where data should be uploaded.
   * @param data - The data to upload.
   * @param metadata - Metadata for the newly uploaded data.
   * @returns A Promise containing an UploadResult
   */
  function uploadBytes(ref, data, metadata) {
      ref._throwIfRoot('uploadBytes');
      const requestInfo = multipartUpload(ref.storage, ref._location, getMappings(), new FbsBlob(data, true), metadata);
      return ref.storage
          .makeRequestWithTokens(requestInfo)
          .then(request => request.getPromise())
          .then(finalMetadata => {
          return {
              metadata: finalMetadata,
              ref
          };
      });
  }
  /**
   * Uploads data to this object's location.
   * The upload can be paused and resumed, and exposes progress updates.
   * @public
   * @param ref - StorageReference where data should be uploaded.
   * @param data - The data to upload.
   * @param metadata - Metadata for the newly uploaded data.
   * @returns An UploadTask
   */
  function uploadBytesResumable(ref, data, metadata) {
      ref._throwIfRoot('uploadBytesResumable');
      return new UploadTask(ref, new FbsBlob(data), metadata);
  }
  /**
   * Uploads a string to this object's location.
   * The upload is not resumable.
   * @public
   * @param ref - StorageReference where string should be uploaded.
   * @param value - The string to upload.
   * @param format - The format of the string to upload.
   * @param metadata - Metadata for the newly uploaded string.
   * @returns A Promise containing an UploadResult
   */
  function uploadString(ref, value, format = StringFormat.RAW, metadata) {
      ref._throwIfRoot('uploadString');
      const data = dataFromString(format, value);
      const metadataClone = Object.assign({}, metadata);
      if (metadataClone['contentType'] == null && data.contentType != null) {
          metadataClone['contentType'] = data.contentType;
      }
      return uploadBytes(ref, data.data, metadataClone);
  }
  /**
   * List all items (files) and prefixes (folders) under this storage reference.
   *
   * This is a helper method for calling list() repeatedly until there are
   * no more results. The default pagination size is 1000.
   *
   * Note: The results may not be consistent if objects are changed while this
   * operation is running.
   *
   * Warning: listAll may potentially consume too many resources if there are
   * too many results.
   * @public
   * @param ref - StorageReference to get list from.
   *
   * @returns A Promise that resolves with all the items and prefixes under
   *      the current storage reference. `prefixes` contains references to
   *      sub-directories and `items` contains references to objects in this
   *      folder. `nextPageToken` is never returned.
   */
  function listAll(ref) {
      const accumulator = {
          prefixes: [],
          items: []
      };
      return listAllHelper(ref, accumulator).then(() => accumulator);
  }
  /**
   * Separated from listAll because async functions can't use "arguments".
   * @param ref
   * @param accumulator
   * @param pageToken
   */
  async function listAllHelper(ref, accumulator, pageToken) {
      const opt = {
          // maxResults is 1000 by default.
          pageToken
      };
      const nextPage = await list$1(ref, opt);
      accumulator.prefixes.push(...nextPage.prefixes);
      accumulator.items.push(...nextPage.items);
      if (nextPage.nextPageToken != null) {
          await listAllHelper(ref, accumulator, nextPage.nextPageToken);
      }
  }
  /**
   * List items (files) and prefixes (folders) under this storage reference.
   *
   * List API is only available for Firebase Rules Version 2.
   *
   * GCS is a key-blob store. Firebase Storage imposes the semantic of '/'
   * delimited folder structure.
   * Refer to GCS's List API if you want to learn more.
   *
   * To adhere to Firebase Rules's Semantics, Firebase Storage does not
   * support objects whose paths end with "/" or contain two consecutive
   * "/"s. Firebase Storage List API will filter these unsupported objects.
   * list() may fail if there are too many unsupported objects in the bucket.
   * @public
   *
   * @param ref - StorageReference to get list from.
   * @param options - See ListOptions for details.
   * @returns A Promise that resolves with the items and prefixes.
   *      `prefixes` contains references to sub-folders and `items`
   *      contains references to objects in this folder. `nextPageToken`
   *      can be used to get the rest of the results.
   */
  async function list$1(ref, options) {
      if (options != null) {
          if (typeof options.maxResults === 'number') {
              validateNumber('options.maxResults', 
              /* minValue= */ 1, 
              /* maxValue= */ 1000, options.maxResults);
          }
      }
      const op = options || {};
      const requestInfo = list(ref.storage, ref._location, 
      /*delimiter= */ '/', op.pageToken, op.maxResults);
      return (await ref.storage.makeRequestWithTokens(requestInfo)).getPromise();
  }
  /**
   * A promise that resolves with the metadata for this object. If this
   * object doesn't exist or metadata cannot be retreived, the promise is
   * rejected.
   * @public
   * @param ref - StorageReference to get metadata from.
   */
  async function getMetadata$1(ref) {
      ref._throwIfRoot('getMetadata');
      const requestInfo = getMetadata(ref.storage, ref._location, getMappings());
      return (await ref.storage.makeRequestWithTokens(requestInfo)).getPromise();
  }
  /**
   * Updates the metadata for this object.
   * @public
   * @param ref - StorageReference to update metadata for.
   * @param metadata - The new metadata for the object.
   *     Only values that have been explicitly set will be changed. Explicitly
   *     setting a value to null will remove the metadata.
   * @returns A promise that resolves
   *     with the new metadata for this object.
   *     See `firebaseStorage.Reference.prototype.getMetadata`
   */
  async function updateMetadata$1(ref, metadata) {
      ref._throwIfRoot('updateMetadata');
      const requestInfo = updateMetadata(ref.storage, ref._location, metadata, getMappings());
      return (await ref.storage.makeRequestWithTokens(requestInfo)).getPromise();
  }
  /**
   * Returns the download URL for the given Reference.
   * @public
   * @returns A promise that resolves with the download
   *     URL for this object.
   */
  async function getDownloadURL(ref) {
      ref._throwIfRoot('getDownloadURL');
      const requestInfo = getDownloadUrl(ref.storage, ref._location, getMappings());
      return (await ref.storage.makeRequestWithTokens(requestInfo))
          .getPromise()
          .then(url => {
          if (url === null) {
              throw noDownloadURL();
          }
          return url;
      });
  }
  /**
   * Deletes the object at this location.
   * @public
   * @param ref - StorageReference for object to delete.
   * @returns A promise that resolves if the deletion succeeds.
   */
  async function deleteObject$1(ref) {
      ref._throwIfRoot('deleteObject');
      const requestInfo = deleteObject(ref.storage, ref._location);
      return (await ref.storage.makeRequestWithTokens(requestInfo)).getPromise();
  }
  /**
   * Returns reference for object obtained by appending `childPath` to `ref`.
   *
   * @param ref - StorageReference to get child of.
   * @param childPath - Child path from provided ref.
   * @returns A reference to the object obtained by
   * appending childPath, removing any duplicate, beginning, or trailing
   * slashes.
   *
   */
  function _getChild(ref, childPath) {
      const newPath = child(ref._location.path, childPath);
      const location = new Location(ref._location.bucket, newPath);
      return new Reference(ref.storage, location);
  }
  
  /**
   * @license
   * Copyright 2017 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 isUrl(path) {
      return /^[A-Za-z]+:\/\//.test(path);
  }
  /**
   * Returns a firebaseStorage.Reference for the given url.
   */
  function refFromURL(service, url) {
      return new Reference(service, url);
  }
  /**
   * Returns a firebaseStorage.Reference for the given path in the default
   * bucket.
   */
  function refFromPath(ref, path) {
      if (ref instanceof StorageService) {
          const service = ref;
          if (service._bucket == null) {
              throw noDefaultBucket();
          }
          const reference = new Reference(service, service._bucket);
          if (path != null) {
              return refFromPath(reference, path);
          }
          else {
              return reference;
          }
      }
      else {
          // ref is a Reference
          if (path !== undefined) {
              if (path.includes('..')) {
                  throw invalidArgument('`path` param cannot contain ".."');
              }
              return _getChild(ref, path);
          }
          else {
              return ref;
          }
      }
  }
  function ref(serviceOrRef, pathOrUrl) {
      if (pathOrUrl && isUrl(pathOrUrl)) {
          if (serviceOrRef instanceof StorageService) {
              return refFromURL(serviceOrRef, pathOrUrl);
          }
          else {
              throw invalidArgument('To use ref(service, url), the first argument must be a Storage instance.');
          }
      }
      else {
          return refFromPath(serviceOrRef, pathOrUrl);
      }
  }
  function extractBucket(host, config) {
      const bucketString = config === null || config === void 0 ? void 0 : config[CONFIG_STORAGE_BUCKET_KEY];
      if (bucketString == null) {
          return null;
      }
      return Location.makeFromBucketSpec(bucketString, host);
  }
  function useStorageEmulator(storage, host, port) {
      storage.host = `http://${host}:${port}`;
  }
  /**
   * A service that provides Firebase Storage Reference instances.
   * @public
   * @param opt_url - gs:// url to a custom Storage Bucket
   */
  class StorageService {
      constructor(
      /**
       * FirebaseApp associated with this StorageService instance.
       */
      app, _authProvider, 
      /**
       * @internal
       */
      _appCheckProvider, 
      /**
       * @internal
       */
      _pool, _url, _firebaseVersion) {
          this.app = app;
          this._authProvider = _authProvider;
          this._appCheckProvider = _appCheckProvider;
          this._pool = _pool;
          this._url = _url;
          this._firebaseVersion = _firebaseVersion;
          this._bucket = null;
          /**
           * This string can be in the formats:
           * - host
           * - host:port
           * - protocol://host:port
           */
          this._host = DEFAULT_HOST;
          this._appId = null;
          this._deleted = false;
          this._maxOperationRetryTime = DEFAULT_MAX_OPERATION_RETRY_TIME;
          this._maxUploadRetryTime = DEFAULT_MAX_UPLOAD_RETRY_TIME;
          this._requests = new Set();
          if (_url != null) {
              this._bucket = Location.makeFromBucketSpec(_url, this._host);
          }
          else {
              this._bucket = extractBucket(this._host, this.app.options);
          }
      }
      get host() {
          return this._host;
      }
      /**
       * Set host string for this service.
       * @param host - host string in the form of host, host:port,
       * or protocol://host:port
       */
      set host(host) {
          this._host = host;
          if (this._url != null) {
              this._bucket = Location.makeFromBucketSpec(this._url, host);
          }
          else {
              this._bucket = extractBucket(host, this.app.options);
          }
      }
      /**
       * The maximum time to retry uploads in milliseconds.
       */
      get maxUploadRetryTime() {
          return this._maxUploadRetryTime;
      }
      set maxUploadRetryTime(time) {
          validateNumber('time', 
          /* minValue=*/ 0, 
          /* maxValue= */ Number.POSITIVE_INFINITY, time);
          this._maxUploadRetryTime = time;
      }
      /**
       * The maximum time to retry operations other than uploads or downloads in
       * milliseconds.
       */
      get maxOperationRetryTime() {
          return this._maxOperationRetryTime;
      }
      set maxOperationRetryTime(time) {
          validateNumber('time', 
          /* minValue=*/ 0, 
          /* maxValue= */ Number.POSITIVE_INFINITY, time);
          this._maxOperationRetryTime = time;
      }
      async _getAuthToken() {
          const auth = this._authProvider.getImmediate({ optional: true });
          if (auth) {
              const tokenData = await auth.getToken();
              if (tokenData !== null) {
                  return tokenData.accessToken;
              }
          }
          return null;
      }
      async _getAppCheckToken() {
          const appCheck = this._appCheckProvider.getImmediate({ optional: true });
          if (appCheck) {
              const result = await appCheck.getToken();
              // TODO: What do we want to do if there is an error getting the token?
              // Context: appCheck.getToken() will never throw even if an error happened. In the error case, a dummy token will be
              // returned along with an error field describing the error. In general, we shouldn't care about the error condition and just use
              // the token (actual or dummy) to send requests.
              return result.token;
          }
          return null;
      }
      /**
       * Stop running requests and prevent more from being created.
       */
      _delete() {
          this._deleted = true;
          this._requests.forEach(request => request.cancel());
          this._requests.clear();
          return Promise.resolve();
      }
      /**
       * Returns a new firebaseStorage.Reference object referencing this StorageService
       * at the given Location.
       */
      _makeStorageReference(loc) {
          return new Reference(this, loc);
      }
      /**
       * @param requestInfo - HTTP RequestInfo object
       * @param authToken - Firebase auth token
       */
      _makeRequest(requestInfo, authToken, appCheckToken) {
          if (!this._deleted) {
              const request = makeRequest(requestInfo, this._appId, authToken, appCheckToken, this._pool, this._firebaseVersion);
              this._requests.add(request);
              // Request removes itself from set when complete.
              request.getPromise().then(() => this._requests.delete(request), () => this._requests.delete(request));
              return request;
          }
          else {
              return new FailRequest(appDeleted());
          }
      }
      async makeRequestWithTokens(requestInfo) {
          const [authToken, appCheckToken] = await Promise.all([
              this._getAuthToken(),
              this._getAppCheckToken()
          ]);
          return this._makeRequest(requestInfo, authToken, appCheckToken);
      }
  }
  
  const name = "@firebase/storage";
  const version = "0.5.5";
  
  /**
   * @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.
   */
  /**
   * Type constant for Firebase Storage.
   */
  const STORAGE_TYPE = 'storage-exp';
  
  /**
   * @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.
   */
  /**
   * Uploads data to this object's location.
   * The upload is not resumable.
   * @public
   * @param ref - StorageReference where data should be uploaded.
   * @param data - The data to upload.
   * @param metadata - Metadata for the data to upload.
   * @returns A Promise containing an UploadResult
   */
  function uploadBytes$1(ref, data, metadata) {
      ref = getModularInstance(ref);
      return uploadBytes(ref, data, metadata);
  }
  /**
   * Uploads a string to this object's location.
   * The upload is not resumable.
   * @public
   * @param ref - StorageReference where string should be uploaded.
   * @param value - The string to upload.
   * @param format - The format of the string to upload.
   * @param metadata - Metadata for the string to upload.
   * @returns A Promise containing an UploadResult
   */
  function uploadString$1(ref, value, format, metadata) {
      ref = getModularInstance(ref);
      return uploadString(ref, value, format, metadata);
  }
  /**
   * Uploads data to this object's location.
   * The upload can be paused and resumed, and exposes progress updates.
   * @public
   * @param ref - StorageReference where data should be uploaded.
   * @param data - The data to upload.
   * @param metadata - Metadata for the data to upload.
   * @returns An UploadTask
   */
  function uploadBytesResumable$1(ref, data, metadata) {
      ref = getModularInstance(ref);
      return uploadBytesResumable(ref, data, metadata);
  }
  /**
   * A promise that resolves with the metadata for this object. If this
   * object doesn't exist or metadata cannot be retreived, the promise is
   * rejected.
   * @public
   * @param ref - StorageReference to get metadata from.
   */
  function getMetadata$2(ref) {
      ref = getModularInstance(ref);
      return getMetadata$1(ref);
  }
  /**
   * Updates the metadata for this object.
   * @public
   * @param ref - StorageReference to update metadata for.
   * @param metadata - The new metadata for the object.
   *     Only values that have been explicitly set will be changed. Explicitly
   *     setting a value to null will remove the metadata.
   * @returns A promise that resolves with the new metadata for this object.
   */
  function updateMetadata$2(ref, metadata) {
      ref = getModularInstance(ref);
      return updateMetadata$1(ref, metadata);
  }
  /**
   * List items (files) and prefixes (folders) under this storage reference.
   *
   * List API is only available for Firebase Rules Version 2.
   *
   * GCS is a key-blob store. Firebase Storage imposes the semantic of '/'
   * delimited folder structure.
   * Refer to GCS's List API if you want to learn more.
   *
   * To adhere to Firebase Rules's Semantics, Firebase Storage does not
   * support objects whose paths end with "/" or contain two consecutive
   * "/"s. Firebase Storage List API will filter these unsupported objects.
   * list() may fail if there are too many unsupported objects in the bucket.
   * @public
   *
   * @param ref - StorageReference to get list from.
   * @param options - See ListOptions for details.
   * @returns A Promise that resolves with the items and prefixes.
   *      `prefixes` contains references to sub-folders and `items`
   *      contains references to objects in this folder. `nextPageToken`
   *      can be used to get the rest of the results.
   */
  function list$2(ref, options) {
      ref = getModularInstance(ref);
      return list$1(ref, options);
  }
  /**
   * List all items (files) and prefixes (folders) under this storage reference.
   *
   * This is a helper method for calling list() repeatedly until there are
   * no more results. The default pagination size is 1000.
   *
   * Note: The results may not be consistent if objects are changed while this
   * operation is running.
   *
   * Warning: listAll may potentially consume too many resources if there are
   * too many results.
   * @public
   * @param ref - StorageReference to get list from.
   *
   * @returns A Promise that resolves with all the items and prefixes under
   *      the current storage reference. `prefixes` contains references to
   *      sub-directories and `items` contains references to objects in this
   *      folder. `nextPageToken` is never returned.
   */
  function listAll$1(ref) {
      ref = getModularInstance(ref);
      return listAll(ref);
  }
  /**
   * Returns the download URL for the given Reference.
   * @public
   * @returns A promise that resolves with the download
   *     URL for this object.
   */
  function getDownloadURL$1(ref) {
      ref = getModularInstance(ref);
      return getDownloadURL(ref);
  }
  /**
   * Deletes the object at this location.
   * @public
   * @param ref - StorageReference for object to delete.
   * @returns A promise that resolves if the deletion succeeds.
   */
  function deleteObject$2(ref) {
      ref = getModularInstance(ref);
      return deleteObject$1(ref);
  }
  function ref$1(serviceOrRef, pathOrUrl) {
      serviceOrRef = getModularInstance(serviceOrRef);
      return ref(serviceOrRef, pathOrUrl);
  }
  /**
   * @internal
   */
  function _getChild$1(ref, childPath) {
      return _getChild(ref, childPath);
  }
  /**
   * Gets a Firebase StorageService instance for the given Firebase app.
   * @public
   * @param app - Firebase app to get Storage instance for.
   * @param bucketUrl - The gs:// url to your Firebase Storage Bucket.
   * If not passed, uses the app's default Storage Bucket.
   * @returns A Firebase StorageService instance.
   */
  function getStorage(app = getApp(), bucketUrl) {
      app = getModularInstance(app);
      const storageProvider = _getProvider(app, STORAGE_TYPE);
      const storageInstance = storageProvider.getImmediate({
          identifier: bucketUrl
      });
      return storageInstance;
  }
  
  /**
   * Cloud Storage for Firebase
   *
   * @packageDocumentation
   */
  /**
   * Modify this `StorageService` instance to communicate with the Cloud Storage emulator.
   *
   * @param storage - The `StorageService` instance
   * @param host - The emulator host (ex: localhost)
   * @param port - The emulator port (ex: 5001)
   * @public
   */
  function useStorageEmulator$1(storage, host, port) {
      useStorageEmulator(storage, host, port);
  }
  function factory(container, { instanceIdentifier: url }) {
      const app = container.getProvider('app-exp').getImmediate();
      const authProvider = container.getProvider('auth-internal');
      const appCheckProvider = container.getProvider('app-check-internal');
      return new StorageService(app, authProvider, appCheckProvider, new XhrIoPool(), url, SDK_VERSION);
  }
  function registerStorage() {
      _registerComponent(new Component(STORAGE_TYPE, factory, "PUBLIC" /* PUBLIC */).setMultipleInstances(true));
      registerVersion(name, version);
  }
  registerStorage();
  
  export { StringFormat, FbsBlob as _FbsBlob, Location as _Location, UploadTask as _UploadTask, _getChild$1 as _getChild, deleteObject$2 as deleteObject, getDownloadURL$1 as getDownloadURL, getMetadata$2 as getMetadata, getStorage, list$2 as list, listAll$1 as listAll, ref$1 as ref, updateMetadata$2 as updateMetadata, uploadBytes$1 as uploadBytes, uploadBytesResumable$1 as uploadBytesResumable, uploadString$1 as uploadString, useStorageEmulator$1 as useStorageEmulator };
  //# sourceMappingURL=index.browser.esm2017.js.map
  