firebase
Version:
Firebase JavaScript library for web and Node.js
1,387 lines (1,365 loc) • 141 kB
JavaScript
import { getApp, _getProvider, _registerComponent, registerVersion, SDK_VERSION } from 'https://www.gstatic.com/firebasejs/9.0.0/firebase-app.js';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
/**
* @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.
*/
var stringToByteArray$1 = function (str) {
// TODO(user): Use native implementations if/when available
var out = [];
var p = 0;
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c < 128) {
out[p++] = c;
}
else if (c < 2048) {
out[p++] = (c >> 6) | 192;
out[p++] = (c & 63) | 128;
}
else if ((c & 0xfc00) === 0xd800 &&
i + 1 < str.length &&
(str.charCodeAt(i + 1) & 0xfc00) === 0xdc00) {
// Surrogate Pair
c = 0x10000 + ((c & 0x03ff) << 10) + (str.charCodeAt(++i) & 0x03ff);
out[p++] = (c >> 18) | 240;
out[p++] = ((c >> 12) & 63) | 128;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
}
else {
out[p++] = (c >> 12) | 224;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
}
}
return out;
};
/**
* Turns an array of numbers into the string given by the concatenation of the
* characters to which the numbers correspond.
* @param bytes Array of numbers representing characters.
* @return Stringification of the array.
*/
var byteArrayToString = function (bytes) {
// TODO(user): Use native implementations if/when available
var out = [];
var pos = 0, c = 0;
while (pos < bytes.length) {
var c1 = bytes[pos++];
if (c1 < 128) {
out[c++] = String.fromCharCode(c1);
}
else if (c1 > 191 && c1 < 224) {
var c2 = bytes[pos++];
out[c++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
}
else if (c1 > 239 && c1 < 365) {
// Surrogate Pair
var c2 = bytes[pos++];
var c3 = bytes[pos++];
var c4 = bytes[pos++];
var u = (((c1 & 7) << 18) | ((c2 & 63) << 12) | ((c3 & 63) << 6) | (c4 & 63)) -
0x10000;
out[c++] = String.fromCharCode(0xd800 + (u >> 10));
out[c++] = String.fromCharCode(0xdc00 + (u & 1023));
}
else {
var c2 = bytes[pos++];
var c3 = bytes[pos++];
out[c++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
}
}
return out.join('');
};
// We define it as an object literal instead of a class because a class compiled down to es5 can't
// be treeshaked. https://github.com/rollup/rollup/issues/1691
// Static lookup maps, lazily populated by init_()
var base64 = {
/**
* Maps bytes to characters.
*/
byteToCharMap_: null,
/**
* Maps characters to bytes.
*/
charToByteMap_: null,
/**
* Maps bytes to websafe characters.
* @private
*/
byteToCharMapWebSafe_: null,
/**
* Maps websafe characters to bytes.
* @private
*/
charToByteMapWebSafe_: null,
/**
* Our default alphabet, shared between
* ENCODED_VALS and ENCODED_VALS_WEBSAFE
*/
ENCODED_VALS_BASE: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789',
/**
* Our default alphabet. Value 64 (=) is special; it means "nothing."
*/
get ENCODED_VALS() {
return this.ENCODED_VALS_BASE + '+/=';
},
/**
* Our websafe alphabet.
*/
get ENCODED_VALS_WEBSAFE() {
return this.ENCODED_VALS_BASE + '-_.';
},
/**
* Whether this browser supports the atob and btoa functions. This extension
* started at Mozilla but is now implemented by many browsers. We use the
* ASSUME_* variables to avoid pulling in the full useragent detection library
* but still allowing the standard per-browser compilations.
*
*/
HAS_NATIVE_SUPPORT: typeof atob === 'function',
/**
* Base64-encode an array of bytes.
*
* @param input An array of bytes (numbers with
* value in [0, 255]) to encode.
* @param webSafe Boolean indicating we should use the
* alternative alphabet.
* @return The base64 encoded string.
*/
encodeByteArray: function (input, webSafe) {
if (!Array.isArray(input)) {
throw Error('encodeByteArray takes an array as a parameter');
}
this.init_();
var byteToCharMap = webSafe
? this.byteToCharMapWebSafe_
: this.byteToCharMap_;
var output = [];
for (var i = 0; i < input.length; i += 3) {
var byte1 = input[i];
var haveByte2 = i + 1 < input.length;
var byte2 = haveByte2 ? input[i + 1] : 0;
var haveByte3 = i + 2 < input.length;
var byte3 = haveByte3 ? input[i + 2] : 0;
var outByte1 = byte1 >> 2;
var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
var outByte3 = ((byte2 & 0x0f) << 2) | (byte3 >> 6);
var outByte4 = byte3 & 0x3f;
if (!haveByte3) {
outByte4 = 64;
if (!haveByte2) {
outByte3 = 64;
}
}
output.push(byteToCharMap[outByte1], byteToCharMap[outByte2], byteToCharMap[outByte3], byteToCharMap[outByte4]);
}
return output.join('');
},
/**
* Base64-encode a string.
*
* @param input A string to encode.
* @param webSafe If true, we should use the
* alternative alphabet.
* @return The base64 encoded string.
*/
encodeString: function (input, webSafe) {
// Shortcut for Mozilla browsers that implement
// a native base64 encoder in the form of "btoa/atob"
if (this.HAS_NATIVE_SUPPORT && !webSafe) {
return btoa(input);
}
return this.encodeByteArray(stringToByteArray$1(input), webSafe);
},
/**
* Base64-decode a string.
*
* @param input to decode.
* @param webSafe True if we should use the
* alternative alphabet.
* @return string representing the decoded value.
*/
decodeString: function (input, webSafe) {
// Shortcut for Mozilla browsers that implement
// a native base64 encoder in the form of "btoa/atob"
if (this.HAS_NATIVE_SUPPORT && !webSafe) {
return atob(input);
}
return byteArrayToString(this.decodeStringToByteArray(input, webSafe));
},
/**
* Base64-decode a string.
*
* In base-64 decoding, groups of four characters are converted into three
* bytes. If the encoder did not apply padding, the input length may not
* be a multiple of 4.
*
* In this case, the last group will have fewer than 4 characters, and
* padding will be inferred. If the group has one or two characters, it decodes
* to one byte. If the group has three characters, it decodes to two bytes.
*
* @param input Input to decode.
* @param webSafe True if we should use the web-safe alphabet.
* @return bytes representing the decoded value.
*/
decodeStringToByteArray: function (input, webSafe) {
this.init_();
var charToByteMap = webSafe
? this.charToByteMapWebSafe_
: this.charToByteMap_;
var output = [];
for (var i = 0; i < input.length;) {
var byte1 = charToByteMap[input.charAt(i++)];
var haveByte2 = i < input.length;
var byte2 = haveByte2 ? charToByteMap[input.charAt(i)] : 0;
++i;
var haveByte3 = i < input.length;
var byte3 = haveByte3 ? charToByteMap[input.charAt(i)] : 64;
++i;
var haveByte4 = i < input.length;
var byte4 = haveByte4 ? charToByteMap[input.charAt(i)] : 64;
++i;
if (byte1 == null || byte2 == null || byte3 == null || byte4 == null) {
throw Error();
}
var outByte1 = (byte1 << 2) | (byte2 >> 4);
output.push(outByte1);
if (byte3 !== 64) {
var outByte2 = ((byte2 << 4) & 0xf0) | (byte3 >> 2);
output.push(outByte2);
if (byte4 !== 64) {
var outByte3 = ((byte3 << 6) & 0xc0) | byte4;
output.push(outByte3);
}
}
}
return output;
},
/**
* Lazy static initialization function. Called before
* accessing any of the static map variables.
* @private
*/
init_: function () {
if (!this.byteToCharMap_) {
this.byteToCharMap_ = {};
this.charToByteMap_ = {};
this.byteToCharMapWebSafe_ = {};
this.charToByteMapWebSafe_ = {};
// We want quick mappings back and forth, so we precompute two maps.
for (var i = 0; i < this.ENCODED_VALS.length; i++) {
this.byteToCharMap_[i] = this.ENCODED_VALS.charAt(i);
this.charToByteMap_[this.byteToCharMap_[i]] = i;
this.byteToCharMapWebSafe_[i] = this.ENCODED_VALS_WEBSAFE.charAt(i);
this.charToByteMapWebSafe_[this.byteToCharMapWebSafe_[i]] = i;
// Be forgiving when decoding and correctly decode both encodings.
if (i >= this.ENCODED_VALS_BASE.length) {
this.charToByteMap_[this.ENCODED_VALS_WEBSAFE.charAt(i)] = i;
this.charToByteMapWebSafe_[this.ENCODED_VALS.charAt(i)] = i;
}
}
}
}
};
/**
* URL-safe base64 encoding
*/
var base64Encode = function (str) {
var utf8Bytes = stringToByteArray$1(str);
return base64.encodeByteArray(utf8Bytes, true);
};
/**
* URL-safe base64 encoding (without "." padding in the end).
* e.g. Used in JSON Web Token (JWT) parts.
*/
var base64urlEncodeWithoutPadding = function (str) {
// Use base64url encoding and remove padding in the end (dot characters).
return base64Encode(str).replace(/\./g, '');
};
/**
* @license
* Copyright 2021 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 createMockUserToken(token, projectId) {
if (token.uid) {
throw new Error('The "uid" field is no longer supported by mockUserToken. Please use "sub" instead for Firebase Auth User ID.');
}
// Unsecured JWTs use "none" as the algorithm.
var header = {
alg: 'none',
type: 'JWT'
};
var project = projectId || 'demo-project';
var iat = token.iat || 0;
var sub = token.sub || token.user_id;
if (!sub) {
throw new Error("mockUserToken must contain 'sub' or 'user_id' field!");
}
var payload = __assign({
// Set all required fields to decent defaults
iss: "https://securetoken.google.com/" + project, aud: project, iat: iat, exp: iat + 3600, auth_time: iat, sub: sub, user_id: sub, firebase: {
sign_in_provider: 'custom',
identities: {}
} }, token);
// Unsecured JWTs use the empty string as a signature.
var signature = '';
return [
base64urlEncodeWithoutPadding(JSON.stringify(header)),
base64urlEncodeWithoutPadding(JSON.stringify(payload)),
signature
].join('.');
}
/**
* @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.
*/
var ERROR_NAME = 'FirebaseError';
// Based on code from:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Custom_Error_Types
var FirebaseError = /** @class */ (function (_super) {
__extends(FirebaseError, _super);
function FirebaseError(code, message, customData) {
var _this = _super.call(this, message) || this;
_this.code = code;
_this.customData = customData;
_this.name = ERROR_NAME;
// Fix For ES5
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
Object.setPrototypeOf(_this, FirebaseError.prototype);
// Maintains proper stack trace for where our error was thrown.
// Only available on V8.
if (Error.captureStackTrace) {
Error.captureStackTrace(_this, ErrorFactory.prototype.create);
}
return _this;
}
return FirebaseError;
}(Error));
var ErrorFactory = /** @class */ (function () {
function ErrorFactory(service, serviceName, errors) {
this.service = service;
this.serviceName = serviceName;
this.errors = errors;
}
ErrorFactory.prototype.create = function (code) {
var data = [];
for (var _i = 1; _i < arguments.length; _i++) {
data[_i - 1] = arguments[_i];
}
var customData = data[0] || {};
var fullCode = this.service + "/" + code;
var template = this.errors[code];
var message = template ? replaceTemplate(template, customData) : 'Error';
// Service Name: Error message (service/code).
var fullMessage = this.serviceName + ": " + message + " (" + fullCode + ").";
var error = new FirebaseError(fullCode, fullMessage, customData);
return error;
};
return ErrorFactory;
}());
function replaceTemplate(template, data) {
return template.replace(PATTERN, function (_, key) {
var value = data[key];
return value != null ? String(value) : "<" + key + "?>";
});
}
var PATTERN = /\{\$([^}]+)}/g;
/**
* @license
* Copyright 2021 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 getModularInstance(service) {
if (service && service._delegate) {
return service._delegate;
}
else {
return service;
}
}
/**
* Component for service name T, e.g. `auth`, `auth-internal`
*/
var Component = /** @class */ (function () {
/**
*
* @param name The public service name, e.g. app, auth, firestore, database
* @param instanceFactory Service factory responsible for creating the public interface
* @param type whether the service provided by the component is public or private
*/
function Component(name, instanceFactory, type) {
this.name = name;
this.instanceFactory = instanceFactory;
this.type = type;
this.multipleInstances = false;
/**
* Properties to be added to the service namespace
*/
this.serviceProps = {};
this.instantiationMode = "LAZY" /* LAZY */;
this.onInstanceCreated = null;
}
Component.prototype.setInstantiationMode = function (mode) {
this.instantiationMode = mode;
return this;
};
Component.prototype.setMultipleInstances = function (multipleInstances) {
this.multipleInstances = multipleInstances;
return this;
};
Component.prototype.setServiceProps = function (props) {
this.serviceProps = props;
return this;
};
Component.prototype.setInstanceCreatedCallback = function (callback) {
this.onInstanceCreated = callback;
return this;
};
return 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 StorageError 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 StorageError.
*/
this.customData = { serverResponse: null };
this._baseMessage = this.message;
// Without this, `instanceof StorageError`, in tests for example,
// returns false.
Object.setPrototypeOf(this, StorageError.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 StorageError("unknown" /* UNKNOWN */, message);
}
function objectNotFound(path) {
return new StorageError("object-not-found" /* OBJECT_NOT_FOUND */, "Object '" + path + "' does not exist.");
}
function quotaExceeded(bucket) {
return new StorageError("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 StorageError("unauthenticated" /* UNAUTHENTICATED */, message);
}
function unauthorizedApp() {
return new StorageError("unauthorized-app" /* UNAUTHORIZED_APP */, 'This app does not have permission to access Firebase Storage on this project.');
}
function unauthorized(path) {
return new StorageError("unauthorized" /* UNAUTHORIZED */, "User does not have permission to access '" + path + "'.");
}
function retryLimitExceeded() {
return new StorageError("retry-limit-exceeded" /* RETRY_LIMIT_EXCEEDED */, 'Max retry time for operation exceeded, please try again.');
}
function canceled() {
return new StorageError("canceled" /* CANCELED */, 'User canceled the upload/download.');
}
function invalidUrl(url) {
return new StorageError("invalid-url" /* INVALID_URL */, "Invalid URL '" + url + "'.");
}
function invalidDefaultBucket(bucket) {
return new StorageError("invalid-default-bucket" /* INVALID_DEFAULT_BUCKET */, "Invalid default bucket '" + bucket + "'.");
}
function noDefaultBucket() {
return new StorageError("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 StorageError("cannot-slice-blob" /* CANNOT_SLICE_BLOB */, 'Cannot slice blob for upload. Please retry the upload.');
}
function serverFileWrongSize() {
return new StorageError("server-file-wrong-size" /* SERVER_FILE_WRONG_SIZE */, 'Server recorded incorrect upload file size, please retry the upload.');
}
function noDownloadURL() {
return new StorageError("no-download-url" /* NO_DOWNLOAD_URL */, 'The given file does not have any download URLs.');
}
/**
* @internal
*/
function invalidArgument(message) {
return new StorageError("invalid-argument" /* INVALID_ARGUMENT */, message);
}
function appDeleted() {
return new StorageError("app-deleted" /* APP_DELETED */, 'The Firebase app was deleted.');
}
/**
* @param name - The name of the operation that was invalid.
*
* @internal
*/
function invalidRootOperation(name) {
return new StorageError("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 StorageError("invalid-format" /* INVALID_FORMAT */, "String does not match format '" + format + "': " + message);
}
/**
* @param message - A message describing the internal error.
*/
function internalError(message) {
throw new StorageError("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.
*/
/**
* Network layer for browsers. We use this instead of goog.net.XhrIo because
* goog.net.XhrIo is hyuuuuge and doesn't work in React Native on Android.
*/
class XhrConnection {
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.xhr_.addEventListener('error', () => {
this.errorCode_ = ErrorCode.NETWORK_ERROR;
resolve();
});
this.xhr_.addEventListener('load', () => {
resolve();
});
});
}
/**
* @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);
}
}
}
function newConnection() {
return new XhrConnection();
}
/**
* @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 ConnectionPool {
createConnection() {
return newConnection();
}
}
/**
* @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.pendingConnection_ = 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 connection = self.pool_.createConnection();
self.pendingConnection_ = connection;
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) {
connection.addUploadProgressListener(progressListener);
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
connection
.send(self.url_, self.method_, self.body_, self.headers_)
.then(() => {
if (self.progressCallback_ !== null) {
connection.removeUploadProgressListener(progressListener);
}
self.pendingConnection_ = null;
const hitServer = connection.getErrorCode() === ErrorCode.NO_ERROR;
const status = connection.getStatus();
if (!hitServer || self.isRetryStatusCode_(status)) {
const wasCanceled = connection.getErrorCode() === ErrorCode.ABORT;
backoffCallback(false, new RequestEndStatus(false, null, wasCanceled));
return;
}
const successCode = self.successCodes_.indexOf(status) !== -1;
backoffCallback(true, new RequestEndStatus(successCode, connection));
});
}
/**
* @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 connection = status.connection;
if (status.wasSuccessCode) {
try {
const result = self.callback_(connection, connection.getResponseText());
if (isJustDef(result)) {
resolve(result);
}
else {
resolve();
}
}
catch (e) {
reject(e);
}
}
else {
if (connection !== null) {
const err = unknown();
err.serverResponse = connection.getResponseText();
if (self.errorCallback_) {
reject(self.errorCallback_(connection, 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.pendingConnection_ !== null) {
this.pendingConnection_.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, connection, canceled) {
this.wasSuccessCode = wasSuccessCode;
this.connection = connection;
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 !== nul