UNPKG

@firebase/storage

Version:

This is the Cloud Storage component of the Firebase JS SDK.

1,366 lines (1,348 loc) 145 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var app = require('@firebase/app'); var tslib = require('tslib'); var util = require('@firebase/util'); var component = require('@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. */ /** * @fileoverview Constants used in the Firebase Storage library. */ /** * Domain name for firebase storage. */ var DEFAULT_HOST = 'firebasestorage.googleapis.com'; /** * The key in Firebase config json for the storage bucket. */ var CONFIG_STORAGE_BUCKET_KEY = 'storageBucket'; /** * 2 minutes * * The timeout for all operations except upload. */ var DEFAULT_MAX_OPERATION_RETRY_TIME = 2 * 60 * 1000; /** * 10 minutes * * The timeout for upload. */ var DEFAULT_MAX_UPLOAD_RETRY_TIME = 10 * 60 * 1000; /** * 1 second */ var DEFAULT_MIN_SLEEP_TIME_MILLIS = 1000; /** * @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 error returned by the Firebase Storage SDK. * @public */ var StorageError = /** @class */ (function (_super) { tslib.__extends(StorageError, _super); /** * @param code - A `StorageErrorCode` string to be prefixed with 'storage/' and * added to the end of the message. * @param message - Error message. * @param status_ - Corresponding HTTP Status Code */ function StorageError(code, message, status_) { if (status_ === void 0) { status_ = 0; } var _this = _super.call(this, prependCode(code), "Firebase Storage: ".concat(message, " (").concat(prependCode(code), ")")) || this; _this.status_ = status_; /** * Stores custom error data unique to the `StorageError`. */ _this.customData = { serverResponse: null }; _this._baseMessage = _this.message; // Without this, `instanceof StorageError`, in tests for example, // returns false. Object.setPrototypeOf(_this, StorageError.prototype); return _this; } Object.defineProperty(StorageError.prototype, "status", { get: function () { return this.status_; }, set: function (status) { this.status_ = status; }, enumerable: false, configurable: true }); /** * Compares a `StorageErrorCode` against this error's code, filtering out the prefix. */ StorageError.prototype._codeEquals = function (code) { return prependCode(code) === this.code; }; Object.defineProperty(StorageError.prototype, "serverResponse", { /** * Optional response message that was added by the server. */ get: function () { return this.customData.serverResponse; }, set: function (serverResponse) { this.customData.serverResponse = serverResponse; if (this.customData.serverResponse) { this.message = "".concat(this._baseMessage, "\n").concat(this.customData.serverResponse); } else { this.message = this._baseMessage; } }, enumerable: false, configurable: true }); return StorageError; }(util.FirebaseError)); /** * @public * Error codes that can be attached to `StorageError` objects. */ exports.StorageErrorCode = void 0; (function (StorageErrorCode) { // Shared between all platforms StorageErrorCode["UNKNOWN"] = "unknown"; StorageErrorCode["OBJECT_NOT_FOUND"] = "object-not-found"; StorageErrorCode["BUCKET_NOT_FOUND"] = "bucket-not-found"; StorageErrorCode["PROJECT_NOT_FOUND"] = "project-not-found"; StorageErrorCode["QUOTA_EXCEEDED"] = "quota-exceeded"; StorageErrorCode["UNAUTHENTICATED"] = "unauthenticated"; StorageErrorCode["UNAUTHORIZED"] = "unauthorized"; StorageErrorCode["UNAUTHORIZED_APP"] = "unauthorized-app"; StorageErrorCode["RETRY_LIMIT_EXCEEDED"] = "retry-limit-exceeded"; StorageErrorCode["INVALID_CHECKSUM"] = "invalid-checksum"; StorageErrorCode["CANCELED"] = "canceled"; // JS specific StorageErrorCode["INVALID_EVENT_NAME"] = "invalid-event-name"; StorageErrorCode["INVALID_URL"] = "invalid-url"; StorageErrorCode["INVALID_DEFAULT_BUCKET"] = "invalid-default-bucket"; StorageErrorCode["NO_DEFAULT_BUCKET"] = "no-default-bucket"; StorageErrorCode["CANNOT_SLICE_BLOB"] = "cannot-slice-blob"; StorageErrorCode["SERVER_FILE_WRONG_SIZE"] = "server-file-wrong-size"; StorageErrorCode["NO_DOWNLOAD_URL"] = "no-download-url"; StorageErrorCode["INVALID_ARGUMENT"] = "invalid-argument"; StorageErrorCode["INVALID_ARGUMENT_COUNT"] = "invalid-argument-count"; StorageErrorCode["APP_DELETED"] = "app-deleted"; StorageErrorCode["INVALID_ROOT_OPERATION"] = "invalid-root-operation"; StorageErrorCode["INVALID_FORMAT"] = "invalid-format"; StorageErrorCode["INTERNAL_ERROR"] = "internal-error"; StorageErrorCode["UNSUPPORTED_ENVIRONMENT"] = "unsupported-environment"; })(exports.StorageErrorCode || (exports.StorageErrorCode = {})); function prependCode(code) { return 'storage/' + code; } function unknown() { var message = 'An unknown error occurred, please check the error payload for ' + 'server response.'; return new StorageError(exports.StorageErrorCode.UNKNOWN, message); } function objectNotFound(path) { return new StorageError(exports.StorageErrorCode.OBJECT_NOT_FOUND, "Object '" + path + "' does not exist."); } function quotaExceeded(bucket) { return new StorageError(exports.StorageErrorCode.QUOTA_EXCEEDED, "Quota for bucket '" + bucket + "' exceeded, please view quota on " + 'https://firebase.google.com/pricing/.'); } function unauthenticated() { var message = 'User is not authenticated, please authenticate using Firebase ' + 'Authentication and try again.'; return new StorageError(exports.StorageErrorCode.UNAUTHENTICATED, message); } function unauthorizedApp() { return new StorageError(exports.StorageErrorCode.UNAUTHORIZED_APP, 'This app does not have permission to access Firebase Storage on this project.'); } function unauthorized(path) { return new StorageError(exports.StorageErrorCode.UNAUTHORIZED, "User does not have permission to access '" + path + "'."); } function retryLimitExceeded() { return new StorageError(exports.StorageErrorCode.RETRY_LIMIT_EXCEEDED, 'Max retry time for operation exceeded, please try again.'); } function canceled() { return new StorageError(exports.StorageErrorCode.CANCELED, 'User canceled the upload/download.'); } function invalidUrl(url) { return new StorageError(exports.StorageErrorCode.INVALID_URL, "Invalid URL '" + url + "'."); } function invalidDefaultBucket(bucket) { return new StorageError(exports.StorageErrorCode.INVALID_DEFAULT_BUCKET, "Invalid default bucket '" + bucket + "'."); } function noDefaultBucket() { return new StorageError(exports.StorageErrorCode.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(exports.StorageErrorCode.CANNOT_SLICE_BLOB, 'Cannot slice blob for upload. Please retry the upload.'); } function serverFileWrongSize() { return new StorageError(exports.StorageErrorCode.SERVER_FILE_WRONG_SIZE, 'Server recorded incorrect upload file size, please retry the upload.'); } function noDownloadURL() { return new StorageError(exports.StorageErrorCode.NO_DOWNLOAD_URL, 'The given file does not have any download URLs.'); } function missingPolyFill(polyFill) { return new StorageError(exports.StorageErrorCode.UNSUPPORTED_ENVIRONMENT, "".concat(polyFill, " is missing. Make sure to install the required polyfills. See https://firebase.google.com/docs/web/environments-js-sdk#polyfills for more information.")); } /** * @internal */ function invalidArgument(message) { return new StorageError(exports.StorageErrorCode.INVALID_ARGUMENT, message); } function appDeleted() { return new StorageError(exports.StorageErrorCode.APP_DELETED, 'The Firebase app was deleted.'); } /** * @param name - The name of the operation that was invalid. * * @internal */ function invalidRootOperation(name) { return new StorageError(exports.StorageErrorCode.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(exports.StorageErrorCode.INVALID_FORMAT, "String does not match format '" + format + "': " + message); } /** * @param message - A message describing the internal error. */ function internalError(message) { throw new StorageError(exports.StorageErrorCode.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. */ /** * Firebase Storage location data. * * @internal */ var Location = /** @class */ (function () { function Location(bucket, path) { this.bucket = bucket; this.path_ = path; } Object.defineProperty(Location.prototype, "path", { get: function () { return this.path_; }, enumerable: false, configurable: true }); Object.defineProperty(Location.prototype, "isRoot", { get: function () { return this.path.length === 0; }, enumerable: false, configurable: true }); Location.prototype.fullServerUrl = function () { var encode = encodeURIComponent; return '/b/' + encode(this.bucket) + '/o/' + encode(this.path); }; Location.prototype.bucketOnlyServerUrl = function () { var encode = encodeURIComponent; return '/b/' + encode(this.bucket) + '/o'; }; Location.makeFromBucketSpec = function (bucketString, host) { var 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); } }; Location.makeFromUrl = function (url, host) { var location = null; var bucketDomain = '([A-Za-z0-9.\\-_]+)'; function gsModify(loc) { if (loc.path.charAt(loc.path.length - 1) === '/') { loc.path_ = loc.path_.slice(0, -1); } } var gsPath = '(/(.*))?$'; var gsRegex = new RegExp('^gs://' + bucketDomain + gsPath, 'i'); var gsIndices = { bucket: 1, path: 3 }; function httpModify(loc) { loc.path_ = decodeURIComponent(loc.path); } var version = 'v[A-Za-z0-9_]+'; var firebaseStorageHost = host.replace(/[.]/g, '\\.'); var firebaseStoragePath = '(/([^?#]*).*)?$'; var firebaseStorageRegExp = new RegExp("^https?://".concat(firebaseStorageHost, "/").concat(version, "/b/").concat(bucketDomain, "/o").concat(firebaseStoragePath), 'i'); var firebaseStorageIndices = { bucket: 1, path: 3 }; var cloudStorageHost = host === DEFAULT_HOST ? '(?:storage.googleapis.com|storage.cloud.google.com)' : host; var cloudStoragePath = '([^?#]*)'; var cloudStorageRegExp = new RegExp("^https?://".concat(cloudStorageHost, "/").concat(bucketDomain, "/").concat(cloudStoragePath), 'i'); var cloudStorageIndices = { bucket: 1, path: 2 }; var groups = [ { regex: gsRegex, indices: gsIndices, postModify: gsModify }, { regex: firebaseStorageRegExp, indices: firebaseStorageIndices, postModify: httpModify }, { regex: cloudStorageRegExp, indices: cloudStorageIndices, postModify: httpModify } ]; for (var i = 0; i < groups.length; i++) { var group = groups[i]; var captures = group.regex.exec(url); if (captures) { var bucketValue = captures[group.indices.bucket]; var 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; }; return Location; }()); /** * A request whose promise always fails. */ var FailRequest = /** @class */ (function () { function FailRequest(error) { this.promise_ = Promise.reject(error); } /** @inheritDoc */ FailRequest.prototype.getPromise = function () { return this.promise_; }; /** @inheritDoc */ FailRequest.prototype.cancel = function (_appDelete) { }; return FailRequest; }()); /** * @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. */ /** * Accepts a callback for an action to perform (`doRequest`), * and then a callback for when the backoff has completed (`backoffCompleteCb`). * The callback sent to start requires an argument to call (`onRequestComplete`). * When `start` calls `doRequest`, it passes a callback for when the request has * completed, `onRequestComplete`. Based on this, the backoff continues, with * another call to `doRequest` and the above loop continues until the timeout * is hit, or a successful response occurs. * @description * @param doRequest Callback to perform request * @param backoffCompleteCb Callback to call when backoff has been completed */ function start(doRequest, // eslint-disable-next-line @typescript-eslint/no-explicit-any backoffCompleteCb, 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) var 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 var retryTimeoutId = null; // eslint-disable-next-line @typescript-eslint/no-explicit-any var globalTimeoutId = null; var hitTimeout = false; var cancelState = 0; function canceled() { return cancelState === 2; } var triggeredCallback = false; function triggerCallback() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (!triggeredCallback) { triggeredCallback = true; backoffCompleteCb.apply(null, args); } } function callWithDelay(millis) { retryTimeoutId = setTimeout(function () { retryTimeoutId = null; doRequest(responseHandler, canceled()); }, millis); } function clearGlobalTimeout() { if (globalTimeoutId) { clearTimeout(globalTimeoutId); } } function responseHandler(success) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } if (triggeredCallback) { clearGlobalTimeout(); return; } if (success) { clearGlobalTimeout(); triggerCallback.call.apply(triggerCallback, tslib.__spreadArray([null, success], args, false)); return; } var mustStop = canceled() || hitTimeout; if (mustStop) { clearGlobalTimeout(); triggerCallback.call.apply(triggerCallback, tslib.__spreadArray([null, success], args, false)); return; } if (waitSeconds < 64) { /* TODO(andysoto): don't back off so quickly if we know we're offline. */ waitSeconds *= 2; } var waitMillis; if (cancelState === 1) { cancelState = 2; waitMillis = 0; } else { waitMillis = (waitSeconds + Math.random()) * 1000; } callWithDelay(waitMillis); } var stopped = false; function stop(wasTimeout) { if (stopped) { return; } stopped = true; clearGlobalTimeout(); if (triggeredCallback) { return; } if (retryTimeoutId !== null) { if (!wasTimeout) { cancelState = 2; } clearTimeout(retryTimeoutId); callWithDelay(0); } else { if (!wasTimeout) { cancelState = 1; } } } callWithDelay(0); globalTimeoutId = setTimeout(function () { 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 '".concat(argument, "'. Expected ").concat(minValue, " or greater.")); } if (value > maxValue) { throw invalidArgument("Invalid value for '".concat(argument, "'. Expected ").concat(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, protocol) { var origin = host; if (protocol == null) { origin = "https://".concat(host); } return "".concat(protocol, "://").concat(origin, "/v0").concat(urlPart); } function makeQueryString(params) { var encode = encodeURIComponent; var queryPart = '?'; for (var key in params) { if (params.hasOwnProperty(key)) { var 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. */ /** * 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 2022 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. */ /** * Checks the status code to see if the action should be retried. * * @param status Current HTTP status code returned by server. * @param additionalRetryCodes additional retry codes to check against */ function isRetryStatusCode(status, additionalRetryCodes) { // The codes for which to retry came from this page: // https://cloud.google.com/storage/docs/exponential-backoff var isFiveHundredCode = status >= 500 && status < 600; var extraRetryCodes = [ // Request Timeout: web server didn't receive full request in time. 408, // Too Many Requests: you're getting rate-limited, basically. 429 ]; var isExtraRetryCode = extraRetryCodes.indexOf(status) !== -1; var isAdditionalRetryCode = additionalRetryCodes.indexOf(status) !== -1; return isFiveHundredCode || isExtraRetryCode || isAdditionalRetryCode; } /** * @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. */ /** * Handles network logic for all Storage Requests, including error reporting and * retries with backoff. * * @param I - the type of the backend's network response. * @param - O the output type used by the rest of the SDK. The conversion * happens in the specified `callback_`. */ var NetworkRequest = /** @class */ (function () { function NetworkRequest(url_, method_, headers_, body_, successCodes_, additionalRetryCodes_, callback_, errorCallback_, timeout_, progressCallback_, connectionFactory_, retry) { if (retry === void 0) { retry = true; } var _this = this; this.url_ = url_; this.method_ = method_; this.headers_ = headers_; this.body_ = body_; this.successCodes_ = successCodes_; this.additionalRetryCodes_ = additionalRetryCodes_; this.callback_ = callback_; this.errorCallback_ = errorCallback_; this.timeout_ = timeout_; this.progressCallback_ = progressCallback_; this.connectionFactory_ = connectionFactory_; this.retry = retry; this.pendingConnection_ = null; this.backoffId_ = null; this.canceled_ = false; this.appDelete_ = false; this.promise_ = new Promise(function (resolve, reject) { _this.resolve_ = resolve; _this.reject_ = reject; _this.start_(); }); } /** * Actually starts the retry loop. */ NetworkRequest.prototype.start_ = function () { var _this = this; var doTheRequest = function (backoffCallback, canceled) { if (canceled) { backoffCallback(false, new RequestEndStatus(false, null, true)); return; } var connection = _this.connectionFactory_(); _this.pendingConnection_ = connection; var progressListener = function (progressEvent) { var loaded = progressEvent.loaded; var total = progressEvent.lengthComputable ? progressEvent.total : -1; if (_this.progressCallback_ !== null) { _this.progressCallback_(loaded, total); } }; if (_this.progressCallback_ !== null) { connection.addUploadProgressListener(progressListener); } // connection.send() never rejects, so we don't need to have a error handler or use catch on the returned promise. // eslint-disable-next-line @typescript-eslint/no-floating-promises connection .send(_this.url_, _this.method_, _this.body_, _this.headers_) .then(function () { if (_this.progressCallback_ !== null) { connection.removeUploadProgressListener(progressListener); } _this.pendingConnection_ = null; var hitServer = connection.getErrorCode() === ErrorCode.NO_ERROR; var status = connection.getStatus(); if (!hitServer || (isRetryStatusCode(status, _this.additionalRetryCodes_) && _this.retry)) { var wasCanceled = connection.getErrorCode() === ErrorCode.ABORT; backoffCallback(false, new RequestEndStatus(false, null, wasCanceled)); return; } var successCode = _this.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. */ var backoffDone = function (requestWentThrough, status) { var resolve = _this.resolve_; var reject = _this.reject_; var connection = status.connection; if (status.wasSuccessCode) { try { var result = _this.callback_(connection, connection.getResponse()); if (isJustDef(result)) { resolve(result); } else { resolve(); } } catch (e) { reject(e); } } else { if (connection !== null) { var err = unknown(); err.serverResponse = connection.getErrorText(); if (_this.errorCallback_) { reject(_this.errorCallback_(connection, err)); } else { reject(err); } } else { if (status.canceled) { var err = _this.appDelete_ ? appDeleted() : canceled(); reject(err); } else { var err = retryLimitExceeded(); reject(err); } } } }; if (this.canceled_) { backoffDone(false, new RequestEndStatus(false, null, true)); } else { this.backoffId_ = start(doTheRequest, backoffDone, this.timeout_); } }; /** @inheritDoc */ NetworkRequest.prototype.getPromise = function () { return this.promise_; }; /** @inheritDoc */ NetworkRequest.prototype.cancel = function (appDelete) { this.canceled_ = true; this.appDelete_ = appDelete || false; if (this.backoffId_ !== null) { stop(this.backoffId_); } if (this.pendingConnection_ !== null) { this.pendingConnection_.abort(); } }; return NetworkRequest; }()); /** * A collection of information about the result of a network request. * @param opt_canceled - Defaults to false. */ var RequestEndStatus = /** @class */ (function () { function RequestEndStatus(wasSuccessCode, connection, canceled) { this.wasSuccessCode = wasSuccessCode; this.connection = connection; this.canceled = !!canceled; } return RequestEndStatus; }()); 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, requestFactory, firebaseVersion, retry) { if (retry === void 0) { retry = true; } var queryPart = makeQueryString(requestInfo.urlParams); var url = requestInfo.url + queryPart; var 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, requestFactory, retry); } /** * @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$1() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var BlobBuilder = getBlobBuilder(); if (BlobBuilder !== undefined) { var bb = new BlobBuilder(); for (var i = 0; i < args.length; i++) { bb.append(args[i]); } return bb.getBlob(); } else { if (isNativeBlobDefined()) { return new Blob(args); } else { throw new StorageError(exports.StorageErrorCode.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 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. */ /** Converts a Base64 encoded string to a binary string. */ function decodeBase64(encoded) { if (typeof atob === 'undefined') { throw missingPolyFill('base-64'); } return atob(encoded); } /** * @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 */ var 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' }; var StringData = /** @class */ (function () { function StringData(data, contentType) { this.data = data; this.contentType = contentType || null; } return StringData; }()); /** * @internal */ 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) { var b = []; for (var i = 0; i < value.length; i++) { var 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. var 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 { var hi = c; var 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) { var 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: { var hasMinus = value.indexOf('-') !== -1; var hasUnder = value.indexOf('_') !== -1; if (hasMinus || hasUnder) { var invalidChar = hasMinus ? '-' : '_'; throw invalidFormat(format, "Invalid character '" + invalidChar + "' found: is it base64url encoded?"); } break; } case StringFormat.BASE64URL: { var hasPlus = value.indexOf('+') !== -1; var hasSlash = value.indexOf('/') !== -1; if (hasPlus || hasSlash) { var invalidChar = hasPlus ? '+' : '/'; throw invalidFormat(format, "Invalid character '" + invalidChar + "' found: is it base64 encoded?"); } value = value.replace(/-/g, '+').replace(/_/g, '/'); break; } // do nothing } var bytes; try { bytes = decodeBase64(value); } catch (e) { if (e.message.includes('polyfill')) { throw e; } throw invalidFormat(format, 'Invalid character found'); } var array = new Uint8Array(bytes.length); for (var i = 0; i < bytes.length; i++) { array[i] = bytes.charCodeAt(i); } return array; } var DataURLParts = /** @class */ (function () { function DataURLParts(dataURL) { this.base64 = false; this.contentType = null; var matches = dataURL.match(/^data:([^,]+)?,/); if (matches === null) { throw invalidFormat(StringFormat.DATA_URL, "Must be formatted 'data:[<mediatype>][;base64],<data>"); } var 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); } return DataURLParts; }()); function dataURLBytes_(dataUrl) { var parts = new DataURLParts(dataUrl); if (parts.base64) { return base64Bytes_(StringFormat.BASE64, parts.rest); } else { return percentEncodedBytes_(parts.rest); } } function dataURLContentType_(dataUrl) { var parts = new DataURLParts(dataUrl); return parts.contentType; } function endsWith(s, end) { var 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 */ var FbsBlob = /** @class */ (function () { function FbsBlob(data, elideCopy) { var size = 0; var 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; } FbsBlob.prototype.size = function () { return this.size_; }; FbsBlob.prototype.type = function () { return this.type_; }; FbsBlob.prototype.slice = function (startByte, endByte) { if (isNativeBlob(this.data_)) { var realBlob = this.data_; var sliced = sliceBlob(realBlob, startByte, endByte); if (sliced === null) { return null; } return new FbsBlob(sliced); } else { var slice = new Uint8Array(this.data_.buffer, startByte, endByte - startByte); return new FbsBlob(slice, true); } }; FbsBlob.getBlob = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (isNativeBlobDefined()) { var blobby = args.map(function (val) { if (val instanceof FbsBlob) { return val.data_; } else { return val; } }); return new FbsBlob(getBlob$1.apply(null, blobby)); } else { var uint8Arrays = args.map(function (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_; } }); var finalLength_1 = 0; uint8Arrays.forEach(function (array) { finalLength_1 += array.byteLength; }); var merged_1 = new Uint8Array(finalLength_1); var index_1 = 0; uint8Arrays.forEach(function (array) { for (var i = 0; i < array.length; i++) { merged_1[index_1++] = array[i]; } }); return new FbsBlob(merged_1, true); } }; FbsBlob.prototype.uploadData = function () { return this.data_; }; return FbsBlob; }()); /** * @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) { var 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 co