@depup/firebase__storage 0.14.1-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/changes.json +10 -0
- package/dist/index.browser.cjs.js +3685 -0
- package/dist/index.browser.cjs.js.map +1 -0
- package/dist/index.cjs.js +3685 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.esm.js +3656 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.node.cjs.js +3710 -0
- package/dist/index.node.cjs.js.map +1 -0
- package/dist/node-esm/index.node.esm.js +3681 -0
- package/dist/node-esm/index.node.esm.js.map +1 -0
- package/dist/node-esm/package.json +1 -0
- package/dist/node-esm/src/api.browser.d.ts +47 -0
- package/dist/node-esm/src/api.d.ts +200 -0
- package/dist/node-esm/src/api.node.d.ts +47 -0
- package/dist/node-esm/src/constants.d.ts +20 -0
- package/dist/node-esm/src/implementation/async.d.ts +22 -0
- package/dist/node-esm/src/implementation/backoff.d.ts +43 -0
- package/dist/node-esm/src/implementation/blob.d.ts +34 -0
- package/dist/node-esm/src/implementation/connection.d.ts +56 -0
- package/dist/node-esm/src/implementation/constants.d.ts +48 -0
- package/dist/node-esm/src/implementation/error.d.ts +126 -0
- package/dist/node-esm/src/implementation/failrequest.d.ts +29 -0
- package/dist/node-esm/src/implementation/fs.d.ts +17 -0
- package/dist/node-esm/src/implementation/json.d.ts +7 -0
- package/dist/node-esm/src/implementation/list.d.ts +19 -0
- package/dist/node-esm/src/implementation/location.d.ts +32 -0
- package/dist/node-esm/src/implementation/metadata.d.ts +40 -0
- package/dist/node-esm/src/implementation/observer.d.ts +47 -0
- package/dist/node-esm/src/implementation/path.d.ts +31 -0
- package/dist/node-esm/src/implementation/request.d.ts +47 -0
- package/dist/node-esm/src/implementation/requestinfo.d.ts +73 -0
- package/dist/node-esm/src/implementation/requests.d.ts +84 -0
- package/dist/node-esm/src/implementation/string.d.ts +73 -0
- package/dist/node-esm/src/implementation/taskenums.d.ts +77 -0
- package/dist/node-esm/src/implementation/type.d.ts +23 -0
- package/dist/node-esm/src/implementation/url.d.ts +22 -0
- package/dist/node-esm/src/implementation/utils.d.ts +23 -0
- package/dist/node-esm/src/index.d.ts +7 -0
- package/dist/node-esm/src/index.node.d.ts +7 -0
- package/dist/node-esm/src/list.d.ts +41 -0
- package/dist/node-esm/src/metadata.d.ts +27 -0
- package/dist/node-esm/src/platform/base64.d.ts +20 -0
- package/dist/node-esm/src/platform/browser/base64.d.ts +19 -0
- package/dist/node-esm/src/platform/browser/connection.d.ts +55 -0
- package/dist/node-esm/src/platform/connection.d.ts +22 -0
- package/dist/node-esm/src/platform/node/base64.d.ts +19 -0
- package/dist/node-esm/src/platform/node/connection.d.ts +58 -0
- package/dist/node-esm/src/public-types.d.ts +424 -0
- package/dist/node-esm/src/reference.d.ts +208 -0
- package/dist/node-esm/src/service.d.ts +131 -0
- package/dist/node-esm/src/task.d.ts +148 -0
- package/dist/node-esm/test/unit/connection.d.ts +47 -0
- package/dist/node-esm/test/unit/testshared.d.ts +60 -0
- package/dist/src/api.browser.d.ts +47 -0
- package/dist/src/api.d.ts +200 -0
- package/dist/src/api.node.d.ts +47 -0
- package/dist/src/constants.d.ts +20 -0
- package/dist/src/implementation/async.d.ts +22 -0
- package/dist/src/implementation/backoff.d.ts +43 -0
- package/dist/src/implementation/blob.d.ts +34 -0
- package/dist/src/implementation/connection.d.ts +56 -0
- package/dist/src/implementation/constants.d.ts +48 -0
- package/dist/src/implementation/error.d.ts +126 -0
- package/dist/src/implementation/failrequest.d.ts +29 -0
- package/dist/src/implementation/fs.d.ts +17 -0
- package/dist/src/implementation/json.d.ts +7 -0
- package/dist/src/implementation/list.d.ts +19 -0
- package/dist/src/implementation/location.d.ts +32 -0
- package/dist/src/implementation/metadata.d.ts +40 -0
- package/dist/src/implementation/observer.d.ts +47 -0
- package/dist/src/implementation/path.d.ts +31 -0
- package/dist/src/implementation/request.d.ts +47 -0
- package/dist/src/implementation/requestinfo.d.ts +73 -0
- package/dist/src/implementation/requests.d.ts +84 -0
- package/dist/src/implementation/string.d.ts +73 -0
- package/dist/src/implementation/taskenums.d.ts +77 -0
- package/dist/src/implementation/type.d.ts +23 -0
- package/dist/src/implementation/url.d.ts +22 -0
- package/dist/src/implementation/utils.d.ts +23 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.node.d.ts +7 -0
- package/dist/src/list.d.ts +41 -0
- package/dist/src/metadata.d.ts +27 -0
- package/dist/src/platform/base64.d.ts +20 -0
- package/dist/src/platform/browser/base64.d.ts +19 -0
- package/dist/src/platform/browser/connection.d.ts +55 -0
- package/dist/src/platform/connection.d.ts +22 -0
- package/dist/src/platform/node/base64.d.ts +19 -0
- package/dist/src/platform/node/connection.d.ts +58 -0
- package/dist/src/public-types.d.ts +424 -0
- package/dist/src/reference.d.ts +208 -0
- package/dist/src/service.d.ts +131 -0
- package/dist/src/task.d.ts +148 -0
- package/dist/src/tsdoc-metadata.json +11 -0
- package/dist/storage-public.d.ts +725 -0
- package/dist/storage.d.ts +1391 -0
- package/dist/test/unit/connection.d.ts +47 -0
- package/dist/test/unit/testshared.d.ts +60 -0
- package/package.json +98 -0
|
@@ -0,0 +1,3710 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var app = require('@firebase/app');
|
|
6
|
+
var util = require('@firebase/util');
|
|
7
|
+
var component = require('@firebase/component');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @license
|
|
11
|
+
* Copyright 2017 Google LLC
|
|
12
|
+
*
|
|
13
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
* you may not use this file except in compliance with the License.
|
|
15
|
+
* You may obtain a copy of the License at
|
|
16
|
+
*
|
|
17
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
*
|
|
19
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
* See the License for the specific language governing permissions and
|
|
23
|
+
* limitations under the License.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* @fileoverview Constants used in the Firebase Storage library.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Domain name for firebase storage.
|
|
30
|
+
*/
|
|
31
|
+
const DEFAULT_HOST = 'firebasestorage.googleapis.com';
|
|
32
|
+
/**
|
|
33
|
+
* The key in Firebase config json for the storage bucket.
|
|
34
|
+
*/
|
|
35
|
+
const CONFIG_STORAGE_BUCKET_KEY = 'storageBucket';
|
|
36
|
+
/**
|
|
37
|
+
* 2 minutes
|
|
38
|
+
*
|
|
39
|
+
* The timeout for all operations except upload.
|
|
40
|
+
*/
|
|
41
|
+
const DEFAULT_MAX_OPERATION_RETRY_TIME = 2 * 60 * 1000;
|
|
42
|
+
/**
|
|
43
|
+
* 10 minutes
|
|
44
|
+
*
|
|
45
|
+
* The timeout for upload.
|
|
46
|
+
*/
|
|
47
|
+
const DEFAULT_MAX_UPLOAD_RETRY_TIME = 10 * 60 * 1000;
|
|
48
|
+
/**
|
|
49
|
+
* 1 second
|
|
50
|
+
*/
|
|
51
|
+
const DEFAULT_MIN_SLEEP_TIME_MILLIS = 1000;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @license
|
|
55
|
+
* Copyright 2017 Google LLC
|
|
56
|
+
*
|
|
57
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
58
|
+
* you may not use this file except in compliance with the License.
|
|
59
|
+
* You may obtain a copy of the License at
|
|
60
|
+
*
|
|
61
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
62
|
+
*
|
|
63
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
64
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
65
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
66
|
+
* See the License for the specific language governing permissions and
|
|
67
|
+
* limitations under the License.
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* An error returned by the Firebase Storage SDK.
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
class StorageError extends util.FirebaseError {
|
|
74
|
+
/**
|
|
75
|
+
* @param code - A `StorageErrorCode` string to be prefixed with 'storage/' and
|
|
76
|
+
* added to the end of the message.
|
|
77
|
+
* @param message - Error message.
|
|
78
|
+
* @param status_ - Corresponding HTTP Status Code
|
|
79
|
+
*/
|
|
80
|
+
constructor(code, message, status_ = 0) {
|
|
81
|
+
super(prependCode(code), `Firebase Storage: ${message} (${prependCode(code)})`);
|
|
82
|
+
this.status_ = status_;
|
|
83
|
+
/**
|
|
84
|
+
* Stores custom error data unique to the `StorageError`.
|
|
85
|
+
*/
|
|
86
|
+
this.customData = { serverResponse: null };
|
|
87
|
+
this._baseMessage = this.message;
|
|
88
|
+
// Without this, `instanceof StorageError`, in tests for example,
|
|
89
|
+
// returns false.
|
|
90
|
+
Object.setPrototypeOf(this, StorageError.prototype);
|
|
91
|
+
}
|
|
92
|
+
get status() {
|
|
93
|
+
return this.status_;
|
|
94
|
+
}
|
|
95
|
+
set status(status) {
|
|
96
|
+
this.status_ = status;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Compares a `StorageErrorCode` against this error's code, filtering out the prefix.
|
|
100
|
+
*/
|
|
101
|
+
_codeEquals(code) {
|
|
102
|
+
return prependCode(code) === this.code;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Optional response message that was added by the server.
|
|
106
|
+
*/
|
|
107
|
+
get serverResponse() {
|
|
108
|
+
return this.customData.serverResponse;
|
|
109
|
+
}
|
|
110
|
+
set serverResponse(serverResponse) {
|
|
111
|
+
this.customData.serverResponse = serverResponse;
|
|
112
|
+
if (this.customData.serverResponse) {
|
|
113
|
+
this.message = `${this._baseMessage}\n${this.customData.serverResponse}`;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this.message = this._baseMessage;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* @public
|
|
122
|
+
* Error codes that can be attached to `StorageError` objects.
|
|
123
|
+
*/
|
|
124
|
+
exports.StorageErrorCode = void 0;
|
|
125
|
+
(function (StorageErrorCode) {
|
|
126
|
+
// Shared between all platforms
|
|
127
|
+
StorageErrorCode["UNKNOWN"] = "unknown";
|
|
128
|
+
StorageErrorCode["OBJECT_NOT_FOUND"] = "object-not-found";
|
|
129
|
+
StorageErrorCode["BUCKET_NOT_FOUND"] = "bucket-not-found";
|
|
130
|
+
StorageErrorCode["PROJECT_NOT_FOUND"] = "project-not-found";
|
|
131
|
+
StorageErrorCode["QUOTA_EXCEEDED"] = "quota-exceeded";
|
|
132
|
+
StorageErrorCode["UNAUTHENTICATED"] = "unauthenticated";
|
|
133
|
+
StorageErrorCode["UNAUTHORIZED"] = "unauthorized";
|
|
134
|
+
StorageErrorCode["UNAUTHORIZED_APP"] = "unauthorized-app";
|
|
135
|
+
StorageErrorCode["RETRY_LIMIT_EXCEEDED"] = "retry-limit-exceeded";
|
|
136
|
+
StorageErrorCode["INVALID_CHECKSUM"] = "invalid-checksum";
|
|
137
|
+
StorageErrorCode["CANCELED"] = "canceled";
|
|
138
|
+
// JS specific
|
|
139
|
+
StorageErrorCode["INVALID_EVENT_NAME"] = "invalid-event-name";
|
|
140
|
+
StorageErrorCode["INVALID_URL"] = "invalid-url";
|
|
141
|
+
StorageErrorCode["INVALID_DEFAULT_BUCKET"] = "invalid-default-bucket";
|
|
142
|
+
StorageErrorCode["NO_DEFAULT_BUCKET"] = "no-default-bucket";
|
|
143
|
+
StorageErrorCode["CANNOT_SLICE_BLOB"] = "cannot-slice-blob";
|
|
144
|
+
StorageErrorCode["SERVER_FILE_WRONG_SIZE"] = "server-file-wrong-size";
|
|
145
|
+
StorageErrorCode["NO_DOWNLOAD_URL"] = "no-download-url";
|
|
146
|
+
StorageErrorCode["INVALID_ARGUMENT"] = "invalid-argument";
|
|
147
|
+
StorageErrorCode["INVALID_ARGUMENT_COUNT"] = "invalid-argument-count";
|
|
148
|
+
StorageErrorCode["APP_DELETED"] = "app-deleted";
|
|
149
|
+
StorageErrorCode["INVALID_ROOT_OPERATION"] = "invalid-root-operation";
|
|
150
|
+
StorageErrorCode["INVALID_FORMAT"] = "invalid-format";
|
|
151
|
+
StorageErrorCode["INTERNAL_ERROR"] = "internal-error";
|
|
152
|
+
StorageErrorCode["UNSUPPORTED_ENVIRONMENT"] = "unsupported-environment";
|
|
153
|
+
})(exports.StorageErrorCode || (exports.StorageErrorCode = {}));
|
|
154
|
+
function prependCode(code) {
|
|
155
|
+
return 'storage/' + code;
|
|
156
|
+
}
|
|
157
|
+
function unknown() {
|
|
158
|
+
const message = 'An unknown error occurred, please check the error payload for ' +
|
|
159
|
+
'server response.';
|
|
160
|
+
return new StorageError(exports.StorageErrorCode.UNKNOWN, message);
|
|
161
|
+
}
|
|
162
|
+
function objectNotFound(path) {
|
|
163
|
+
return new StorageError(exports.StorageErrorCode.OBJECT_NOT_FOUND, "Object '" + path + "' does not exist.");
|
|
164
|
+
}
|
|
165
|
+
function quotaExceeded(bucket) {
|
|
166
|
+
return new StorageError(exports.StorageErrorCode.QUOTA_EXCEEDED, "Quota for bucket '" +
|
|
167
|
+
bucket +
|
|
168
|
+
"' exceeded, please view quota on " +
|
|
169
|
+
'https://firebase.google.com/pricing/.');
|
|
170
|
+
}
|
|
171
|
+
function unauthenticated() {
|
|
172
|
+
const message = 'User is not authenticated, please authenticate using Firebase ' +
|
|
173
|
+
'Authentication and try again.';
|
|
174
|
+
return new StorageError(exports.StorageErrorCode.UNAUTHENTICATED, message);
|
|
175
|
+
}
|
|
176
|
+
function unauthorizedApp() {
|
|
177
|
+
return new StorageError(exports.StorageErrorCode.UNAUTHORIZED_APP, 'This app does not have permission to access Firebase Storage on this project.');
|
|
178
|
+
}
|
|
179
|
+
function unauthorized(path) {
|
|
180
|
+
return new StorageError(exports.StorageErrorCode.UNAUTHORIZED, "User does not have permission to access '" + path + "'.");
|
|
181
|
+
}
|
|
182
|
+
function retryLimitExceeded() {
|
|
183
|
+
return new StorageError(exports.StorageErrorCode.RETRY_LIMIT_EXCEEDED, 'Max retry time for operation exceeded, please try again.');
|
|
184
|
+
}
|
|
185
|
+
function canceled() {
|
|
186
|
+
return new StorageError(exports.StorageErrorCode.CANCELED, 'User canceled the upload/download.');
|
|
187
|
+
}
|
|
188
|
+
function invalidUrl(url) {
|
|
189
|
+
return new StorageError(exports.StorageErrorCode.INVALID_URL, "Invalid URL '" + url + "'.");
|
|
190
|
+
}
|
|
191
|
+
function invalidDefaultBucket(bucket) {
|
|
192
|
+
return new StorageError(exports.StorageErrorCode.INVALID_DEFAULT_BUCKET, "Invalid default bucket '" + bucket + "'.");
|
|
193
|
+
}
|
|
194
|
+
function noDefaultBucket() {
|
|
195
|
+
return new StorageError(exports.StorageErrorCode.NO_DEFAULT_BUCKET, 'No default bucket ' +
|
|
196
|
+
"found. Did you set the '" +
|
|
197
|
+
CONFIG_STORAGE_BUCKET_KEY +
|
|
198
|
+
"' property when initializing the app?");
|
|
199
|
+
}
|
|
200
|
+
function cannotSliceBlob() {
|
|
201
|
+
return new StorageError(exports.StorageErrorCode.CANNOT_SLICE_BLOB, 'Cannot slice blob for upload. Please retry the upload.');
|
|
202
|
+
}
|
|
203
|
+
function serverFileWrongSize() {
|
|
204
|
+
return new StorageError(exports.StorageErrorCode.SERVER_FILE_WRONG_SIZE, 'Server recorded incorrect upload file size, please retry the upload.');
|
|
205
|
+
}
|
|
206
|
+
function noDownloadURL() {
|
|
207
|
+
return new StorageError(exports.StorageErrorCode.NO_DOWNLOAD_URL, 'The given file does not have any download URLs.');
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* @internal
|
|
211
|
+
*/
|
|
212
|
+
function invalidArgument(message) {
|
|
213
|
+
return new StorageError(exports.StorageErrorCode.INVALID_ARGUMENT, message);
|
|
214
|
+
}
|
|
215
|
+
function appDeleted() {
|
|
216
|
+
return new StorageError(exports.StorageErrorCode.APP_DELETED, 'The Firebase app was deleted.');
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* @param name - The name of the operation that was invalid.
|
|
220
|
+
*
|
|
221
|
+
* @internal
|
|
222
|
+
*/
|
|
223
|
+
function invalidRootOperation(name) {
|
|
224
|
+
return new StorageError(exports.StorageErrorCode.INVALID_ROOT_OPERATION, "The operation '" +
|
|
225
|
+
name +
|
|
226
|
+
"' cannot be performed on a root reference, create a non-root " +
|
|
227
|
+
"reference using child, such as .child('file.png').");
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* @param format - The format that was not valid.
|
|
231
|
+
* @param message - A message describing the format violation.
|
|
232
|
+
*/
|
|
233
|
+
function invalidFormat(format, message) {
|
|
234
|
+
return new StorageError(exports.StorageErrorCode.INVALID_FORMAT, "String does not match format '" + format + "': " + message);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* @param message - A message describing the internal error.
|
|
238
|
+
*/
|
|
239
|
+
function internalError(message) {
|
|
240
|
+
throw new StorageError(exports.StorageErrorCode.INTERNAL_ERROR, 'Internal error: ' + message);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* @license
|
|
245
|
+
* Copyright 2017 Google LLC
|
|
246
|
+
*
|
|
247
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
248
|
+
* you may not use this file except in compliance with the License.
|
|
249
|
+
* You may obtain a copy of the License at
|
|
250
|
+
*
|
|
251
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
252
|
+
*
|
|
253
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
254
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
255
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
256
|
+
* See the License for the specific language governing permissions and
|
|
257
|
+
* limitations under the License.
|
|
258
|
+
*/
|
|
259
|
+
/**
|
|
260
|
+
* Firebase Storage location data.
|
|
261
|
+
*
|
|
262
|
+
* @internal
|
|
263
|
+
*/
|
|
264
|
+
class Location {
|
|
265
|
+
constructor(bucket, path) {
|
|
266
|
+
this.bucket = bucket;
|
|
267
|
+
this.path_ = path;
|
|
268
|
+
}
|
|
269
|
+
get path() {
|
|
270
|
+
return this.path_;
|
|
271
|
+
}
|
|
272
|
+
get isRoot() {
|
|
273
|
+
return this.path.length === 0;
|
|
274
|
+
}
|
|
275
|
+
fullServerUrl() {
|
|
276
|
+
const encode = encodeURIComponent;
|
|
277
|
+
return '/b/' + encode(this.bucket) + '/o/' + encode(this.path);
|
|
278
|
+
}
|
|
279
|
+
bucketOnlyServerUrl() {
|
|
280
|
+
const encode = encodeURIComponent;
|
|
281
|
+
return '/b/' + encode(this.bucket) + '/o';
|
|
282
|
+
}
|
|
283
|
+
static makeFromBucketSpec(bucketString, host) {
|
|
284
|
+
let bucketLocation;
|
|
285
|
+
try {
|
|
286
|
+
bucketLocation = Location.makeFromUrl(bucketString, host);
|
|
287
|
+
}
|
|
288
|
+
catch (e) {
|
|
289
|
+
// Not valid URL, use as-is. This lets you put bare bucket names in
|
|
290
|
+
// config.
|
|
291
|
+
return new Location(bucketString, '');
|
|
292
|
+
}
|
|
293
|
+
if (bucketLocation.path === '') {
|
|
294
|
+
return bucketLocation;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
throw invalidDefaultBucket(bucketString);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
static makeFromUrl(url, host) {
|
|
301
|
+
let location = null;
|
|
302
|
+
const bucketDomain = '([A-Za-z0-9.\\-_]+)';
|
|
303
|
+
function gsModify(loc) {
|
|
304
|
+
if (loc.path.charAt(loc.path.length - 1) === '/') {
|
|
305
|
+
loc.path_ = loc.path_.slice(0, -1);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const gsPath = '(/(.*))?$';
|
|
309
|
+
const gsRegex = new RegExp('^gs://' + bucketDomain + gsPath, 'i');
|
|
310
|
+
const gsIndices = { bucket: 1, path: 3 };
|
|
311
|
+
function httpModify(loc) {
|
|
312
|
+
loc.path_ = decodeURIComponent(loc.path);
|
|
313
|
+
}
|
|
314
|
+
const version = 'v[A-Za-z0-9_]+';
|
|
315
|
+
const firebaseStorageHost = host.replace(/[.]/g, '\\.');
|
|
316
|
+
const firebaseStoragePath = '(/([^?#]*).*)?$';
|
|
317
|
+
const firebaseStorageRegExp = new RegExp(`^https?://${firebaseStorageHost}/${version}/b/${bucketDomain}/o${firebaseStoragePath}`, 'i');
|
|
318
|
+
const firebaseStorageIndices = { bucket: 1, path: 3 };
|
|
319
|
+
const cloudStorageHost = host === DEFAULT_HOST
|
|
320
|
+
? '(?:storage.googleapis.com|storage.cloud.google.com)'
|
|
321
|
+
: host;
|
|
322
|
+
const cloudStoragePath = '([^?#]*)';
|
|
323
|
+
const cloudStorageRegExp = new RegExp(`^https?://${cloudStorageHost}/${bucketDomain}/${cloudStoragePath}`, 'i');
|
|
324
|
+
const cloudStorageIndices = { bucket: 1, path: 2 };
|
|
325
|
+
const groups = [
|
|
326
|
+
{ regex: gsRegex, indices: gsIndices, postModify: gsModify },
|
|
327
|
+
{
|
|
328
|
+
regex: firebaseStorageRegExp,
|
|
329
|
+
indices: firebaseStorageIndices,
|
|
330
|
+
postModify: httpModify
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
regex: cloudStorageRegExp,
|
|
334
|
+
indices: cloudStorageIndices,
|
|
335
|
+
postModify: httpModify
|
|
336
|
+
}
|
|
337
|
+
];
|
|
338
|
+
for (let i = 0; i < groups.length; i++) {
|
|
339
|
+
const group = groups[i];
|
|
340
|
+
const captures = group.regex.exec(url);
|
|
341
|
+
if (captures) {
|
|
342
|
+
const bucketValue = captures[group.indices.bucket];
|
|
343
|
+
let pathValue = captures[group.indices.path];
|
|
344
|
+
if (!pathValue) {
|
|
345
|
+
pathValue = '';
|
|
346
|
+
}
|
|
347
|
+
location = new Location(bucketValue, pathValue);
|
|
348
|
+
group.postModify(location);
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (location == null) {
|
|
353
|
+
throw invalidUrl(url);
|
|
354
|
+
}
|
|
355
|
+
return location;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* A request whose promise always fails.
|
|
361
|
+
*/
|
|
362
|
+
class FailRequest {
|
|
363
|
+
constructor(error) {
|
|
364
|
+
this.promise_ = Promise.reject(error);
|
|
365
|
+
}
|
|
366
|
+
/** @inheritDoc */
|
|
367
|
+
getPromise() {
|
|
368
|
+
return this.promise_;
|
|
369
|
+
}
|
|
370
|
+
/** @inheritDoc */
|
|
371
|
+
cancel(_appDelete = false) { }
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* @license
|
|
376
|
+
* Copyright 2017 Google LLC
|
|
377
|
+
*
|
|
378
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
379
|
+
* you may not use this file except in compliance with the License.
|
|
380
|
+
* You may obtain a copy of the License at
|
|
381
|
+
*
|
|
382
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
383
|
+
*
|
|
384
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
385
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
386
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
387
|
+
* See the License for the specific language governing permissions and
|
|
388
|
+
* limitations under the License.
|
|
389
|
+
*/
|
|
390
|
+
/**
|
|
391
|
+
* Accepts a callback for an action to perform (`doRequest`),
|
|
392
|
+
* and then a callback for when the backoff has completed (`backoffCompleteCb`).
|
|
393
|
+
* The callback sent to start requires an argument to call (`onRequestComplete`).
|
|
394
|
+
* When `start` calls `doRequest`, it passes a callback for when the request has
|
|
395
|
+
* completed, `onRequestComplete`. Based on this, the backoff continues, with
|
|
396
|
+
* another call to `doRequest` and the above loop continues until the timeout
|
|
397
|
+
* is hit, or a successful response occurs.
|
|
398
|
+
* @description
|
|
399
|
+
* @param doRequest Callback to perform request
|
|
400
|
+
* @param backoffCompleteCb Callback to call when backoff has been completed
|
|
401
|
+
*/
|
|
402
|
+
function start(doRequest,
|
|
403
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
404
|
+
backoffCompleteCb, timeout) {
|
|
405
|
+
// TODO(andysoto): make this code cleaner (probably refactor into an actual
|
|
406
|
+
// type instead of a bunch of functions with state shared in the closure)
|
|
407
|
+
let waitSeconds = 1;
|
|
408
|
+
// Would type this as "number" but that doesn't work for Node so ¯\_(ツ)_/¯
|
|
409
|
+
// TODO: find a way to exclude Node type definition for storage because storage only works in browser
|
|
410
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
411
|
+
let retryTimeoutId = null;
|
|
412
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
413
|
+
let globalTimeoutId = null;
|
|
414
|
+
let hitTimeout = false;
|
|
415
|
+
let cancelState = 0;
|
|
416
|
+
function canceled() {
|
|
417
|
+
return cancelState === 2;
|
|
418
|
+
}
|
|
419
|
+
let triggeredCallback = false;
|
|
420
|
+
function triggerCallback(...args) {
|
|
421
|
+
if (!triggeredCallback) {
|
|
422
|
+
triggeredCallback = true;
|
|
423
|
+
backoffCompleteCb.apply(null, args);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function callWithDelay(millis) {
|
|
427
|
+
retryTimeoutId = setTimeout(() => {
|
|
428
|
+
retryTimeoutId = null;
|
|
429
|
+
doRequest(responseHandler, canceled());
|
|
430
|
+
}, millis);
|
|
431
|
+
}
|
|
432
|
+
function clearGlobalTimeout() {
|
|
433
|
+
if (globalTimeoutId) {
|
|
434
|
+
clearTimeout(globalTimeoutId);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function responseHandler(success, ...args) {
|
|
438
|
+
if (triggeredCallback) {
|
|
439
|
+
clearGlobalTimeout();
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (success) {
|
|
443
|
+
clearGlobalTimeout();
|
|
444
|
+
triggerCallback.call(null, success, ...args);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const mustStop = canceled() || hitTimeout;
|
|
448
|
+
if (mustStop) {
|
|
449
|
+
clearGlobalTimeout();
|
|
450
|
+
triggerCallback.call(null, success, ...args);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (waitSeconds < 64) {
|
|
454
|
+
/* TODO(andysoto): don't back off so quickly if we know we're offline. */
|
|
455
|
+
waitSeconds *= 2;
|
|
456
|
+
}
|
|
457
|
+
let waitMillis;
|
|
458
|
+
if (cancelState === 1) {
|
|
459
|
+
cancelState = 2;
|
|
460
|
+
waitMillis = 0;
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
waitMillis = (waitSeconds + Math.random()) * 1000;
|
|
464
|
+
}
|
|
465
|
+
callWithDelay(waitMillis);
|
|
466
|
+
}
|
|
467
|
+
let stopped = false;
|
|
468
|
+
function stop(wasTimeout) {
|
|
469
|
+
if (stopped) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
stopped = true;
|
|
473
|
+
clearGlobalTimeout();
|
|
474
|
+
if (triggeredCallback) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (retryTimeoutId !== null) {
|
|
478
|
+
if (!wasTimeout) {
|
|
479
|
+
cancelState = 2;
|
|
480
|
+
}
|
|
481
|
+
clearTimeout(retryTimeoutId);
|
|
482
|
+
callWithDelay(0);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
if (!wasTimeout) {
|
|
486
|
+
cancelState = 1;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
callWithDelay(0);
|
|
491
|
+
globalTimeoutId = setTimeout(() => {
|
|
492
|
+
hitTimeout = true;
|
|
493
|
+
stop(true);
|
|
494
|
+
}, timeout);
|
|
495
|
+
return stop;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Stops the retry loop from repeating.
|
|
499
|
+
* If the function is currently "in between" retries, it is invoked immediately
|
|
500
|
+
* with the second parameter as "true". Otherwise, it will be invoked once more
|
|
501
|
+
* after the current invocation finishes iff the current invocation would have
|
|
502
|
+
* triggered another retry.
|
|
503
|
+
*/
|
|
504
|
+
function stop(id) {
|
|
505
|
+
id(false);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* @license
|
|
510
|
+
* Copyright 2017 Google LLC
|
|
511
|
+
*
|
|
512
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
513
|
+
* you may not use this file except in compliance with the License.
|
|
514
|
+
* You may obtain a copy of the License at
|
|
515
|
+
*
|
|
516
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
517
|
+
*
|
|
518
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
519
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
520
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
521
|
+
* See the License for the specific language governing permissions and
|
|
522
|
+
* limitations under the License.
|
|
523
|
+
*/
|
|
524
|
+
function isJustDef(p) {
|
|
525
|
+
return p !== void 0;
|
|
526
|
+
}
|
|
527
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
528
|
+
function isFunction(p) {
|
|
529
|
+
return typeof p === 'function';
|
|
530
|
+
}
|
|
531
|
+
function isNonArrayObject(p) {
|
|
532
|
+
return typeof p === 'object' && !Array.isArray(p);
|
|
533
|
+
}
|
|
534
|
+
function isString(p) {
|
|
535
|
+
return typeof p === 'string' || p instanceof String;
|
|
536
|
+
}
|
|
537
|
+
function isNativeBlob(p) {
|
|
538
|
+
return isNativeBlobDefined() && p instanceof Blob;
|
|
539
|
+
}
|
|
540
|
+
function isNativeBlobDefined() {
|
|
541
|
+
return typeof Blob !== 'undefined';
|
|
542
|
+
}
|
|
543
|
+
function validateNumber(argument, minValue, maxValue, value) {
|
|
544
|
+
if (value < minValue) {
|
|
545
|
+
throw invalidArgument(`Invalid value for '${argument}'. Expected ${minValue} or greater.`);
|
|
546
|
+
}
|
|
547
|
+
if (value > maxValue) {
|
|
548
|
+
throw invalidArgument(`Invalid value for '${argument}'. Expected ${maxValue} or less.`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* @license
|
|
554
|
+
* Copyright 2017 Google LLC
|
|
555
|
+
*
|
|
556
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
557
|
+
* you may not use this file except in compliance with the License.
|
|
558
|
+
* You may obtain a copy of the License at
|
|
559
|
+
*
|
|
560
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
561
|
+
*
|
|
562
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
563
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
564
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
565
|
+
* See the License for the specific language governing permissions and
|
|
566
|
+
* limitations under the License.
|
|
567
|
+
*/
|
|
568
|
+
function makeUrl(urlPart, host, protocol) {
|
|
569
|
+
let origin = host;
|
|
570
|
+
if (protocol == null) {
|
|
571
|
+
origin = `https://${host}`;
|
|
572
|
+
}
|
|
573
|
+
return `${protocol}://${origin}/v0${urlPart}`;
|
|
574
|
+
}
|
|
575
|
+
function makeQueryString(params) {
|
|
576
|
+
const encode = encodeURIComponent;
|
|
577
|
+
let queryPart = '?';
|
|
578
|
+
for (const key in params) {
|
|
579
|
+
if (params.hasOwnProperty(key)) {
|
|
580
|
+
const nextPart = encode(key) + '=' + encode(params[key]);
|
|
581
|
+
queryPart = queryPart + nextPart + '&';
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Chop off the extra '&' or '?' on the end
|
|
585
|
+
queryPart = queryPart.slice(0, -1);
|
|
586
|
+
return queryPart;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Error codes for requests made by the XhrIo wrapper.
|
|
591
|
+
*/
|
|
592
|
+
var ErrorCode;
|
|
593
|
+
(function (ErrorCode) {
|
|
594
|
+
ErrorCode[ErrorCode["NO_ERROR"] = 0] = "NO_ERROR";
|
|
595
|
+
ErrorCode[ErrorCode["NETWORK_ERROR"] = 1] = "NETWORK_ERROR";
|
|
596
|
+
ErrorCode[ErrorCode["ABORT"] = 2] = "ABORT";
|
|
597
|
+
})(ErrorCode || (ErrorCode = {}));
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* @license
|
|
601
|
+
* Copyright 2022 Google LLC
|
|
602
|
+
*
|
|
603
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
604
|
+
* you may not use this file except in compliance with the License.
|
|
605
|
+
* You may obtain a copy of the License at
|
|
606
|
+
*
|
|
607
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
608
|
+
*
|
|
609
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
610
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
611
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
612
|
+
* See the License for the specific language governing permissions and
|
|
613
|
+
* limitations under the License.
|
|
614
|
+
*/
|
|
615
|
+
/**
|
|
616
|
+
* Checks the status code to see if the action should be retried.
|
|
617
|
+
*
|
|
618
|
+
* @param status Current HTTP status code returned by server.
|
|
619
|
+
* @param additionalRetryCodes additional retry codes to check against
|
|
620
|
+
*/
|
|
621
|
+
function isRetryStatusCode(status, additionalRetryCodes) {
|
|
622
|
+
// The codes for which to retry came from this page:
|
|
623
|
+
// https://cloud.google.com/storage/docs/exponential-backoff
|
|
624
|
+
const isFiveHundredCode = status >= 500 && status < 600;
|
|
625
|
+
const extraRetryCodes = [
|
|
626
|
+
// Request Timeout: web server didn't receive full request in time.
|
|
627
|
+
408,
|
|
628
|
+
// Too Many Requests: you're getting rate-limited, basically.
|
|
629
|
+
429
|
|
630
|
+
];
|
|
631
|
+
const isExtraRetryCode = extraRetryCodes.indexOf(status) !== -1;
|
|
632
|
+
const isAdditionalRetryCode = additionalRetryCodes.indexOf(status) !== -1;
|
|
633
|
+
return isFiveHundredCode || isExtraRetryCode || isAdditionalRetryCode;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* @license
|
|
638
|
+
* Copyright 2017 Google LLC
|
|
639
|
+
*
|
|
640
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
641
|
+
* you may not use this file except in compliance with the License.
|
|
642
|
+
* You may obtain a copy of the License at
|
|
643
|
+
*
|
|
644
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
645
|
+
*
|
|
646
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
647
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
648
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
649
|
+
* See the License for the specific language governing permissions and
|
|
650
|
+
* limitations under the License.
|
|
651
|
+
*/
|
|
652
|
+
/**
|
|
653
|
+
* Handles network logic for all Storage Requests, including error reporting and
|
|
654
|
+
* retries with backoff.
|
|
655
|
+
*
|
|
656
|
+
* @param I - the type of the backend's network response.
|
|
657
|
+
* @param - O the output type used by the rest of the SDK. The conversion
|
|
658
|
+
* happens in the specified `callback_`.
|
|
659
|
+
*/
|
|
660
|
+
class NetworkRequest {
|
|
661
|
+
constructor(url_, method_, headers_, body_, successCodes_, additionalRetryCodes_, callback_, errorCallback_, timeout_, progressCallback_, connectionFactory_, retry = true, isUsingEmulator = false) {
|
|
662
|
+
this.url_ = url_;
|
|
663
|
+
this.method_ = method_;
|
|
664
|
+
this.headers_ = headers_;
|
|
665
|
+
this.body_ = body_;
|
|
666
|
+
this.successCodes_ = successCodes_;
|
|
667
|
+
this.additionalRetryCodes_ = additionalRetryCodes_;
|
|
668
|
+
this.callback_ = callback_;
|
|
669
|
+
this.errorCallback_ = errorCallback_;
|
|
670
|
+
this.timeout_ = timeout_;
|
|
671
|
+
this.progressCallback_ = progressCallback_;
|
|
672
|
+
this.connectionFactory_ = connectionFactory_;
|
|
673
|
+
this.retry = retry;
|
|
674
|
+
this.isUsingEmulator = isUsingEmulator;
|
|
675
|
+
this.pendingConnection_ = null;
|
|
676
|
+
this.backoffId_ = null;
|
|
677
|
+
this.canceled_ = false;
|
|
678
|
+
this.appDelete_ = false;
|
|
679
|
+
this.promise_ = new Promise((resolve, reject) => {
|
|
680
|
+
this.resolve_ = resolve;
|
|
681
|
+
this.reject_ = reject;
|
|
682
|
+
this.start_();
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Actually starts the retry loop.
|
|
687
|
+
*/
|
|
688
|
+
start_() {
|
|
689
|
+
const doTheRequest = (backoffCallback, canceled) => {
|
|
690
|
+
if (canceled) {
|
|
691
|
+
backoffCallback(false, new RequestEndStatus(false, null, true));
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
const connection = this.connectionFactory_();
|
|
695
|
+
this.pendingConnection_ = connection;
|
|
696
|
+
const progressListener = progressEvent => {
|
|
697
|
+
const loaded = progressEvent.loaded;
|
|
698
|
+
const total = progressEvent.lengthComputable ? progressEvent.total : -1;
|
|
699
|
+
if (this.progressCallback_ !== null) {
|
|
700
|
+
this.progressCallback_(loaded, total);
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
if (this.progressCallback_ !== null) {
|
|
704
|
+
connection.addUploadProgressListener(progressListener);
|
|
705
|
+
}
|
|
706
|
+
// connection.send() never rejects, so we don't need to have a error handler or use catch on the returned promise.
|
|
707
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
708
|
+
connection
|
|
709
|
+
.send(this.url_, this.method_, this.isUsingEmulator, this.body_, this.headers_)
|
|
710
|
+
.then(() => {
|
|
711
|
+
if (this.progressCallback_ !== null) {
|
|
712
|
+
connection.removeUploadProgressListener(progressListener);
|
|
713
|
+
}
|
|
714
|
+
this.pendingConnection_ = null;
|
|
715
|
+
const hitServer = connection.getErrorCode() === ErrorCode.NO_ERROR;
|
|
716
|
+
const status = connection.getStatus();
|
|
717
|
+
if (!hitServer ||
|
|
718
|
+
(isRetryStatusCode(status, this.additionalRetryCodes_) &&
|
|
719
|
+
this.retry)) {
|
|
720
|
+
const wasCanceled = connection.getErrorCode() === ErrorCode.ABORT;
|
|
721
|
+
backoffCallback(false, new RequestEndStatus(false, null, wasCanceled));
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
const successCode = this.successCodes_.indexOf(status) !== -1;
|
|
725
|
+
backoffCallback(true, new RequestEndStatus(successCode, connection));
|
|
726
|
+
});
|
|
727
|
+
};
|
|
728
|
+
/**
|
|
729
|
+
* @param requestWentThrough - True if the request eventually went
|
|
730
|
+
* through, false if it hit the retry limit or was canceled.
|
|
731
|
+
*/
|
|
732
|
+
const backoffDone = (requestWentThrough, status) => {
|
|
733
|
+
const resolve = this.resolve_;
|
|
734
|
+
const reject = this.reject_;
|
|
735
|
+
const connection = status.connection;
|
|
736
|
+
if (status.wasSuccessCode) {
|
|
737
|
+
try {
|
|
738
|
+
const result = this.callback_(connection, connection.getResponse());
|
|
739
|
+
if (isJustDef(result)) {
|
|
740
|
+
resolve(result);
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
resolve();
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
catch (e) {
|
|
747
|
+
reject(e);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
if (connection !== null) {
|
|
752
|
+
const err = unknown();
|
|
753
|
+
err.serverResponse = connection.getErrorText();
|
|
754
|
+
if (this.errorCallback_) {
|
|
755
|
+
reject(this.errorCallback_(connection, err));
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
reject(err);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
if (status.canceled) {
|
|
763
|
+
const err = this.appDelete_ ? appDeleted() : canceled();
|
|
764
|
+
reject(err);
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
const err = retryLimitExceeded();
|
|
768
|
+
reject(err);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
if (this.canceled_) {
|
|
774
|
+
backoffDone(false, new RequestEndStatus(false, null, true));
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
this.backoffId_ = start(doTheRequest, backoffDone, this.timeout_);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
/** @inheritDoc */
|
|
781
|
+
getPromise() {
|
|
782
|
+
return this.promise_;
|
|
783
|
+
}
|
|
784
|
+
/** @inheritDoc */
|
|
785
|
+
cancel(appDelete) {
|
|
786
|
+
this.canceled_ = true;
|
|
787
|
+
this.appDelete_ = appDelete || false;
|
|
788
|
+
if (this.backoffId_ !== null) {
|
|
789
|
+
stop(this.backoffId_);
|
|
790
|
+
}
|
|
791
|
+
if (this.pendingConnection_ !== null) {
|
|
792
|
+
this.pendingConnection_.abort();
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* A collection of information about the result of a network request.
|
|
798
|
+
* @param opt_canceled - Defaults to false.
|
|
799
|
+
*/
|
|
800
|
+
class RequestEndStatus {
|
|
801
|
+
constructor(wasSuccessCode, connection, canceled) {
|
|
802
|
+
this.wasSuccessCode = wasSuccessCode;
|
|
803
|
+
this.connection = connection;
|
|
804
|
+
this.canceled = !!canceled;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
function addAuthHeader_(headers, authToken) {
|
|
808
|
+
if (authToken !== null && authToken.length > 0) {
|
|
809
|
+
headers['Authorization'] = 'Firebase ' + authToken;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function addVersionHeader_(headers, firebaseVersion) {
|
|
813
|
+
headers['X-Firebase-Storage-Version'] =
|
|
814
|
+
'webjs/' + (firebaseVersion ?? 'AppManager');
|
|
815
|
+
}
|
|
816
|
+
function addGmpidHeader_(headers, appId) {
|
|
817
|
+
if (appId) {
|
|
818
|
+
headers['X-Firebase-GMPID'] = appId;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
function addAppCheckHeader_(headers, appCheckToken) {
|
|
822
|
+
if (appCheckToken !== null) {
|
|
823
|
+
headers['X-Firebase-AppCheck'] = appCheckToken;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
function makeRequest(requestInfo, appId, authToken, appCheckToken, requestFactory, firebaseVersion, retry = true, isUsingEmulator = false) {
|
|
827
|
+
const queryPart = makeQueryString(requestInfo.urlParams);
|
|
828
|
+
const url = requestInfo.url + queryPart;
|
|
829
|
+
const headers = Object.assign({}, requestInfo.headers);
|
|
830
|
+
addGmpidHeader_(headers, appId);
|
|
831
|
+
addAuthHeader_(headers, authToken);
|
|
832
|
+
addVersionHeader_(headers, firebaseVersion);
|
|
833
|
+
addAppCheckHeader_(headers, appCheckToken);
|
|
834
|
+
return new NetworkRequest(url, requestInfo.method, headers, requestInfo.body, requestInfo.successCodes, requestInfo.additionalRetryCodes, requestInfo.handler, requestInfo.errorHandler, requestInfo.timeout, requestInfo.progressCallback, requestFactory, retry, isUsingEmulator);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/**
|
|
838
|
+
* @license
|
|
839
|
+
* Copyright 2017 Google LLC
|
|
840
|
+
*
|
|
841
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
842
|
+
* you may not use this file except in compliance with the License.
|
|
843
|
+
* You may obtain a copy of the License at
|
|
844
|
+
*
|
|
845
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
846
|
+
*
|
|
847
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
848
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
849
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
850
|
+
* See the License for the specific language governing permissions and
|
|
851
|
+
* limitations under the License.
|
|
852
|
+
*/
|
|
853
|
+
function getBlobBuilder() {
|
|
854
|
+
if (typeof BlobBuilder !== 'undefined') {
|
|
855
|
+
return BlobBuilder;
|
|
856
|
+
}
|
|
857
|
+
else if (typeof WebKitBlobBuilder !== 'undefined') {
|
|
858
|
+
return WebKitBlobBuilder;
|
|
859
|
+
}
|
|
860
|
+
else {
|
|
861
|
+
return undefined;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Concatenates one or more values together and converts them to a Blob.
|
|
866
|
+
*
|
|
867
|
+
* @param args The values that will make up the resulting blob.
|
|
868
|
+
* @return The blob.
|
|
869
|
+
*/
|
|
870
|
+
function getBlob$1(...args) {
|
|
871
|
+
const BlobBuilder = getBlobBuilder();
|
|
872
|
+
if (BlobBuilder !== undefined) {
|
|
873
|
+
const bb = new BlobBuilder();
|
|
874
|
+
for (let i = 0; i < args.length; i++) {
|
|
875
|
+
bb.append(args[i]);
|
|
876
|
+
}
|
|
877
|
+
return bb.getBlob();
|
|
878
|
+
}
|
|
879
|
+
else {
|
|
880
|
+
if (isNativeBlobDefined()) {
|
|
881
|
+
return new Blob(args);
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
throw new StorageError(exports.StorageErrorCode.UNSUPPORTED_ENVIRONMENT, "This browser doesn't seem to support creating Blobs");
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Slices the blob. The returned blob contains data from the start byte
|
|
890
|
+
* (inclusive) till the end byte (exclusive). Negative indices cannot be used.
|
|
891
|
+
*
|
|
892
|
+
* @param blob The blob to be sliced.
|
|
893
|
+
* @param start Index of the starting byte.
|
|
894
|
+
* @param end Index of the ending byte.
|
|
895
|
+
* @return The blob slice or null if not supported.
|
|
896
|
+
*/
|
|
897
|
+
function sliceBlob(blob, start, end) {
|
|
898
|
+
if (blob.webkitSlice) {
|
|
899
|
+
return blob.webkitSlice(start, end);
|
|
900
|
+
}
|
|
901
|
+
else if (blob.mozSlice) {
|
|
902
|
+
return blob.mozSlice(start, end);
|
|
903
|
+
}
|
|
904
|
+
else if (blob.slice) {
|
|
905
|
+
return blob.slice(start, end);
|
|
906
|
+
}
|
|
907
|
+
return null;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* @license
|
|
912
|
+
* Copyright 2021 Google LLC
|
|
913
|
+
*
|
|
914
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
915
|
+
* you may not use this file except in compliance with the License.
|
|
916
|
+
* You may obtain a copy of the License at
|
|
917
|
+
*
|
|
918
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
919
|
+
*
|
|
920
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
921
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
922
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
923
|
+
* See the License for the specific language governing permissions and
|
|
924
|
+
* limitations under the License.
|
|
925
|
+
*/
|
|
926
|
+
/** Converts a Base64 encoded string to a binary string. */
|
|
927
|
+
function decodeBase64(encoded) {
|
|
928
|
+
// Node actually doesn't validate base64 strings.
|
|
929
|
+
// A quick sanity check that is not a fool-proof validation
|
|
930
|
+
if (/[^-A-Za-z0-9+/=]/.test(encoded)) {
|
|
931
|
+
throw invalidFormat('base64', 'Invalid character found');
|
|
932
|
+
}
|
|
933
|
+
return Buffer.from(encoded, 'base64').toString('binary');
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* @license
|
|
938
|
+
* Copyright 2017 Google LLC
|
|
939
|
+
*
|
|
940
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
941
|
+
* you may not use this file except in compliance with the License.
|
|
942
|
+
* You may obtain a copy of the License at
|
|
943
|
+
*
|
|
944
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
945
|
+
*
|
|
946
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
947
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
948
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
949
|
+
* See the License for the specific language governing permissions and
|
|
950
|
+
* limitations under the License.
|
|
951
|
+
*/
|
|
952
|
+
/**
|
|
953
|
+
* An enumeration of the possible string formats for upload.
|
|
954
|
+
* @public
|
|
955
|
+
*/
|
|
956
|
+
const StringFormat = {
|
|
957
|
+
/**
|
|
958
|
+
* Indicates the string should be interpreted "raw", that is, as normal text.
|
|
959
|
+
* The string will be interpreted as UTF-16, then uploaded as a UTF-8 byte
|
|
960
|
+
* sequence.
|
|
961
|
+
* Example: The string 'Hello! \\ud83d\\ude0a' becomes the byte sequence
|
|
962
|
+
* 48 65 6c 6c 6f 21 20 f0 9f 98 8a
|
|
963
|
+
*/
|
|
964
|
+
RAW: 'raw',
|
|
965
|
+
/**
|
|
966
|
+
* Indicates the string should be interpreted as base64-encoded data.
|
|
967
|
+
* Padding characters (trailing '='s) are optional.
|
|
968
|
+
* Example: The string 'rWmO++E6t7/rlw==' becomes the byte sequence
|
|
969
|
+
* ad 69 8e fb e1 3a b7 bf eb 97
|
|
970
|
+
*/
|
|
971
|
+
BASE64: 'base64',
|
|
972
|
+
/**
|
|
973
|
+
* Indicates the string should be interpreted as base64url-encoded data.
|
|
974
|
+
* Padding characters (trailing '='s) are optional.
|
|
975
|
+
* Example: The string 'rWmO--E6t7_rlw==' becomes the byte sequence
|
|
976
|
+
* ad 69 8e fb e1 3a b7 bf eb 97
|
|
977
|
+
*/
|
|
978
|
+
BASE64URL: 'base64url',
|
|
979
|
+
/**
|
|
980
|
+
* Indicates the string is a data URL, such as one obtained from
|
|
981
|
+
* canvas.toDataURL().
|
|
982
|
+
* Example: the string 'data:application/octet-stream;base64,aaaa'
|
|
983
|
+
* becomes the byte sequence
|
|
984
|
+
* 69 a6 9a
|
|
985
|
+
* (the content-type "application/octet-stream" is also applied, but can
|
|
986
|
+
* be overridden in the metadata object).
|
|
987
|
+
*/
|
|
988
|
+
DATA_URL: 'data_url'
|
|
989
|
+
};
|
|
990
|
+
class StringData {
|
|
991
|
+
constructor(data, contentType) {
|
|
992
|
+
this.data = data;
|
|
993
|
+
this.contentType = contentType || null;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* @internal
|
|
998
|
+
*/
|
|
999
|
+
function dataFromString(format, stringData) {
|
|
1000
|
+
switch (format) {
|
|
1001
|
+
case StringFormat.RAW:
|
|
1002
|
+
return new StringData(utf8Bytes_(stringData));
|
|
1003
|
+
case StringFormat.BASE64:
|
|
1004
|
+
case StringFormat.BASE64URL:
|
|
1005
|
+
return new StringData(base64Bytes_(format, stringData));
|
|
1006
|
+
case StringFormat.DATA_URL:
|
|
1007
|
+
return new StringData(dataURLBytes_(stringData), dataURLContentType_(stringData));
|
|
1008
|
+
// do nothing
|
|
1009
|
+
}
|
|
1010
|
+
// assert(false);
|
|
1011
|
+
throw unknown();
|
|
1012
|
+
}
|
|
1013
|
+
function utf8Bytes_(value) {
|
|
1014
|
+
const b = [];
|
|
1015
|
+
for (let i = 0; i < value.length; i++) {
|
|
1016
|
+
let c = value.charCodeAt(i);
|
|
1017
|
+
if (c <= 127) {
|
|
1018
|
+
b.push(c);
|
|
1019
|
+
}
|
|
1020
|
+
else {
|
|
1021
|
+
if (c <= 2047) {
|
|
1022
|
+
b.push(192 | (c >> 6), 128 | (c & 63));
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
if ((c & 64512) === 55296) {
|
|
1026
|
+
// The start of a surrogate pair.
|
|
1027
|
+
const valid = i < value.length - 1 && (value.charCodeAt(i + 1) & 64512) === 56320;
|
|
1028
|
+
if (!valid) {
|
|
1029
|
+
// The second surrogate wasn't there.
|
|
1030
|
+
b.push(239, 191, 189);
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
const hi = c;
|
|
1034
|
+
const lo = value.charCodeAt(++i);
|
|
1035
|
+
c = 65536 | ((hi & 1023) << 10) | (lo & 1023);
|
|
1036
|
+
b.push(240 | (c >> 18), 128 | ((c >> 12) & 63), 128 | ((c >> 6) & 63), 128 | (c & 63));
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
else {
|
|
1040
|
+
if ((c & 64512) === 56320) {
|
|
1041
|
+
// Invalid low surrogate.
|
|
1042
|
+
b.push(239, 191, 189);
|
|
1043
|
+
}
|
|
1044
|
+
else {
|
|
1045
|
+
b.push(224 | (c >> 12), 128 | ((c >> 6) & 63), 128 | (c & 63));
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return new Uint8Array(b);
|
|
1052
|
+
}
|
|
1053
|
+
function percentEncodedBytes_(value) {
|
|
1054
|
+
let decoded;
|
|
1055
|
+
try {
|
|
1056
|
+
decoded = decodeURIComponent(value);
|
|
1057
|
+
}
|
|
1058
|
+
catch (e) {
|
|
1059
|
+
throw invalidFormat(StringFormat.DATA_URL, 'Malformed data URL.');
|
|
1060
|
+
}
|
|
1061
|
+
return utf8Bytes_(decoded);
|
|
1062
|
+
}
|
|
1063
|
+
function base64Bytes_(format, value) {
|
|
1064
|
+
switch (format) {
|
|
1065
|
+
case StringFormat.BASE64: {
|
|
1066
|
+
const hasMinus = value.indexOf('-') !== -1;
|
|
1067
|
+
const hasUnder = value.indexOf('_') !== -1;
|
|
1068
|
+
if (hasMinus || hasUnder) {
|
|
1069
|
+
const invalidChar = hasMinus ? '-' : '_';
|
|
1070
|
+
throw invalidFormat(format, "Invalid character '" +
|
|
1071
|
+
invalidChar +
|
|
1072
|
+
"' found: is it base64url encoded?");
|
|
1073
|
+
}
|
|
1074
|
+
break;
|
|
1075
|
+
}
|
|
1076
|
+
case StringFormat.BASE64URL: {
|
|
1077
|
+
const hasPlus = value.indexOf('+') !== -1;
|
|
1078
|
+
const hasSlash = value.indexOf('/') !== -1;
|
|
1079
|
+
if (hasPlus || hasSlash) {
|
|
1080
|
+
const invalidChar = hasPlus ? '+' : '/';
|
|
1081
|
+
throw invalidFormat(format, "Invalid character '" + invalidChar + "' found: is it base64 encoded?");
|
|
1082
|
+
}
|
|
1083
|
+
value = value.replace(/-/g, '+').replace(/_/g, '/');
|
|
1084
|
+
break;
|
|
1085
|
+
}
|
|
1086
|
+
// do nothing
|
|
1087
|
+
}
|
|
1088
|
+
let bytes;
|
|
1089
|
+
try {
|
|
1090
|
+
bytes = decodeBase64(value);
|
|
1091
|
+
}
|
|
1092
|
+
catch (e) {
|
|
1093
|
+
if (e.message.includes('polyfill')) {
|
|
1094
|
+
throw e;
|
|
1095
|
+
}
|
|
1096
|
+
throw invalidFormat(format, 'Invalid character found');
|
|
1097
|
+
}
|
|
1098
|
+
const array = new Uint8Array(bytes.length);
|
|
1099
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1100
|
+
array[i] = bytes.charCodeAt(i);
|
|
1101
|
+
}
|
|
1102
|
+
return array;
|
|
1103
|
+
}
|
|
1104
|
+
class DataURLParts {
|
|
1105
|
+
constructor(dataURL) {
|
|
1106
|
+
this.base64 = false;
|
|
1107
|
+
this.contentType = null;
|
|
1108
|
+
const matches = dataURL.match(/^data:([^,]+)?,/);
|
|
1109
|
+
if (matches === null) {
|
|
1110
|
+
throw invalidFormat(StringFormat.DATA_URL, "Must be formatted 'data:[<mediatype>][;base64],<data>");
|
|
1111
|
+
}
|
|
1112
|
+
const middle = matches[1] || null;
|
|
1113
|
+
if (middle != null) {
|
|
1114
|
+
this.base64 = endsWith(middle, ';base64');
|
|
1115
|
+
this.contentType = this.base64
|
|
1116
|
+
? middle.substring(0, middle.length - ';base64'.length)
|
|
1117
|
+
: middle;
|
|
1118
|
+
}
|
|
1119
|
+
this.rest = dataURL.substring(dataURL.indexOf(',') + 1);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
function dataURLBytes_(dataUrl) {
|
|
1123
|
+
const parts = new DataURLParts(dataUrl);
|
|
1124
|
+
if (parts.base64) {
|
|
1125
|
+
return base64Bytes_(StringFormat.BASE64, parts.rest);
|
|
1126
|
+
}
|
|
1127
|
+
else {
|
|
1128
|
+
return percentEncodedBytes_(parts.rest);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
function dataURLContentType_(dataUrl) {
|
|
1132
|
+
const parts = new DataURLParts(dataUrl);
|
|
1133
|
+
return parts.contentType;
|
|
1134
|
+
}
|
|
1135
|
+
function endsWith(s, end) {
|
|
1136
|
+
const longEnough = s.length >= end.length;
|
|
1137
|
+
if (!longEnough) {
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
1140
|
+
return s.substring(s.length - end.length) === end;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* @license
|
|
1145
|
+
* Copyright 2017 Google LLC
|
|
1146
|
+
*
|
|
1147
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1148
|
+
* you may not use this file except in compliance with the License.
|
|
1149
|
+
* You may obtain a copy of the License at
|
|
1150
|
+
*
|
|
1151
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1152
|
+
*
|
|
1153
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1154
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1155
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1156
|
+
* See the License for the specific language governing permissions and
|
|
1157
|
+
* limitations under the License.
|
|
1158
|
+
*/
|
|
1159
|
+
/**
|
|
1160
|
+
* @param opt_elideCopy - If true, doesn't copy mutable input data
|
|
1161
|
+
* (e.g. Uint8Arrays). Pass true only if you know the objects will not be
|
|
1162
|
+
* modified after this blob's construction.
|
|
1163
|
+
*
|
|
1164
|
+
* @internal
|
|
1165
|
+
*/
|
|
1166
|
+
class FbsBlob {
|
|
1167
|
+
constructor(data, elideCopy) {
|
|
1168
|
+
let size = 0;
|
|
1169
|
+
let blobType = '';
|
|
1170
|
+
if (isNativeBlob(data)) {
|
|
1171
|
+
this.data_ = data;
|
|
1172
|
+
size = data.size;
|
|
1173
|
+
blobType = data.type;
|
|
1174
|
+
}
|
|
1175
|
+
else if (data instanceof ArrayBuffer) {
|
|
1176
|
+
if (elideCopy) {
|
|
1177
|
+
this.data_ = new Uint8Array(data);
|
|
1178
|
+
}
|
|
1179
|
+
else {
|
|
1180
|
+
this.data_ = new Uint8Array(data.byteLength);
|
|
1181
|
+
this.data_.set(new Uint8Array(data));
|
|
1182
|
+
}
|
|
1183
|
+
size = this.data_.length;
|
|
1184
|
+
}
|
|
1185
|
+
else if (data instanceof Uint8Array) {
|
|
1186
|
+
if (elideCopy) {
|
|
1187
|
+
this.data_ = data;
|
|
1188
|
+
}
|
|
1189
|
+
else {
|
|
1190
|
+
this.data_ = new Uint8Array(data.length);
|
|
1191
|
+
this.data_.set(data);
|
|
1192
|
+
}
|
|
1193
|
+
size = data.length;
|
|
1194
|
+
}
|
|
1195
|
+
this.size_ = size;
|
|
1196
|
+
this.type_ = blobType;
|
|
1197
|
+
}
|
|
1198
|
+
size() {
|
|
1199
|
+
return this.size_;
|
|
1200
|
+
}
|
|
1201
|
+
type() {
|
|
1202
|
+
return this.type_;
|
|
1203
|
+
}
|
|
1204
|
+
slice(startByte, endByte) {
|
|
1205
|
+
if (isNativeBlob(this.data_)) {
|
|
1206
|
+
const realBlob = this.data_;
|
|
1207
|
+
const sliced = sliceBlob(realBlob, startByte, endByte);
|
|
1208
|
+
if (sliced === null) {
|
|
1209
|
+
return null;
|
|
1210
|
+
}
|
|
1211
|
+
return new FbsBlob(sliced);
|
|
1212
|
+
}
|
|
1213
|
+
else {
|
|
1214
|
+
const slice = new Uint8Array(this.data_.buffer, startByte, endByte - startByte);
|
|
1215
|
+
return new FbsBlob(slice, true);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
static getBlob(...args) {
|
|
1219
|
+
if (isNativeBlobDefined()) {
|
|
1220
|
+
const blobby = args.map((val) => {
|
|
1221
|
+
if (val instanceof FbsBlob) {
|
|
1222
|
+
return val.data_;
|
|
1223
|
+
}
|
|
1224
|
+
else {
|
|
1225
|
+
return val;
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
return new FbsBlob(getBlob$1.apply(null, blobby));
|
|
1229
|
+
}
|
|
1230
|
+
else {
|
|
1231
|
+
const uint8Arrays = args.map((val) => {
|
|
1232
|
+
if (isString(val)) {
|
|
1233
|
+
return dataFromString(StringFormat.RAW, val).data;
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
// Blobs don't exist, so this has to be a Uint8Array.
|
|
1237
|
+
return val.data_;
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
let finalLength = 0;
|
|
1241
|
+
uint8Arrays.forEach((array) => {
|
|
1242
|
+
finalLength += array.byteLength;
|
|
1243
|
+
});
|
|
1244
|
+
const merged = new Uint8Array(finalLength);
|
|
1245
|
+
let index = 0;
|
|
1246
|
+
uint8Arrays.forEach((array) => {
|
|
1247
|
+
for (let i = 0; i < array.length; i++) {
|
|
1248
|
+
merged[index++] = array[i];
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
return new FbsBlob(merged, true);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
uploadData() {
|
|
1255
|
+
return this.data_;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/**
|
|
1260
|
+
* @license
|
|
1261
|
+
* Copyright 2017 Google LLC
|
|
1262
|
+
*
|
|
1263
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1264
|
+
* you may not use this file except in compliance with the License.
|
|
1265
|
+
* You may obtain a copy of the License at
|
|
1266
|
+
*
|
|
1267
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1268
|
+
*
|
|
1269
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1270
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1271
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1272
|
+
* See the License for the specific language governing permissions and
|
|
1273
|
+
* limitations under the License.
|
|
1274
|
+
*/
|
|
1275
|
+
/**
|
|
1276
|
+
* Returns the Object resulting from parsing the given JSON, or null if the
|
|
1277
|
+
* given string does not represent a JSON object.
|
|
1278
|
+
*/
|
|
1279
|
+
function jsonObjectOrNull(s) {
|
|
1280
|
+
let obj;
|
|
1281
|
+
try {
|
|
1282
|
+
obj = JSON.parse(s);
|
|
1283
|
+
}
|
|
1284
|
+
catch (e) {
|
|
1285
|
+
return null;
|
|
1286
|
+
}
|
|
1287
|
+
if (isNonArrayObject(obj)) {
|
|
1288
|
+
return obj;
|
|
1289
|
+
}
|
|
1290
|
+
else {
|
|
1291
|
+
return null;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* @license
|
|
1297
|
+
* Copyright 2017 Google LLC
|
|
1298
|
+
*
|
|
1299
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1300
|
+
* you may not use this file except in compliance with the License.
|
|
1301
|
+
* You may obtain a copy of the License at
|
|
1302
|
+
*
|
|
1303
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1304
|
+
*
|
|
1305
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1306
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1307
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1308
|
+
* See the License for the specific language governing permissions and
|
|
1309
|
+
* limitations under the License.
|
|
1310
|
+
*/
|
|
1311
|
+
/**
|
|
1312
|
+
* @fileoverview Contains helper methods for manipulating paths.
|
|
1313
|
+
*/
|
|
1314
|
+
/**
|
|
1315
|
+
* @return Null if the path is already at the root.
|
|
1316
|
+
*/
|
|
1317
|
+
function parent(path) {
|
|
1318
|
+
if (path.length === 0) {
|
|
1319
|
+
return null;
|
|
1320
|
+
}
|
|
1321
|
+
const index = path.lastIndexOf('/');
|
|
1322
|
+
if (index === -1) {
|
|
1323
|
+
return '';
|
|
1324
|
+
}
|
|
1325
|
+
const newPath = path.slice(0, index);
|
|
1326
|
+
return newPath;
|
|
1327
|
+
}
|
|
1328
|
+
function child(path, childPath) {
|
|
1329
|
+
const canonicalChildPath = childPath
|
|
1330
|
+
.split('/')
|
|
1331
|
+
.filter(component => component.length > 0)
|
|
1332
|
+
.join('/');
|
|
1333
|
+
if (path.length === 0) {
|
|
1334
|
+
return canonicalChildPath;
|
|
1335
|
+
}
|
|
1336
|
+
else {
|
|
1337
|
+
return path + '/' + canonicalChildPath;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Returns the last component of a path.
|
|
1342
|
+
* '/foo/bar' -> 'bar'
|
|
1343
|
+
* '/foo/bar/baz/' -> 'baz/'
|
|
1344
|
+
* '/a' -> 'a'
|
|
1345
|
+
*/
|
|
1346
|
+
function lastComponent(path) {
|
|
1347
|
+
const index = path.lastIndexOf('/', path.length - 2);
|
|
1348
|
+
if (index === -1) {
|
|
1349
|
+
return path;
|
|
1350
|
+
}
|
|
1351
|
+
else {
|
|
1352
|
+
return path.slice(index + 1);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
/**
|
|
1357
|
+
* @license
|
|
1358
|
+
* Copyright 2017 Google LLC
|
|
1359
|
+
*
|
|
1360
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1361
|
+
* you may not use this file except in compliance with the License.
|
|
1362
|
+
* You may obtain a copy of the License at
|
|
1363
|
+
*
|
|
1364
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1365
|
+
*
|
|
1366
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1367
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1368
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1369
|
+
* See the License for the specific language governing permissions and
|
|
1370
|
+
* limitations under the License.
|
|
1371
|
+
*/
|
|
1372
|
+
function noXform_(metadata, value) {
|
|
1373
|
+
return value;
|
|
1374
|
+
}
|
|
1375
|
+
class Mapping {
|
|
1376
|
+
constructor(server, local, writable, xform) {
|
|
1377
|
+
this.server = server;
|
|
1378
|
+
this.local = local || server;
|
|
1379
|
+
this.writable = !!writable;
|
|
1380
|
+
this.xform = xform || noXform_;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
let mappings_ = null;
|
|
1384
|
+
function xformPath(fullPath) {
|
|
1385
|
+
if (!isString(fullPath) || fullPath.length < 2) {
|
|
1386
|
+
return fullPath;
|
|
1387
|
+
}
|
|
1388
|
+
else {
|
|
1389
|
+
return lastComponent(fullPath);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
function getMappings() {
|
|
1393
|
+
if (mappings_) {
|
|
1394
|
+
return mappings_;
|
|
1395
|
+
}
|
|
1396
|
+
const mappings = [];
|
|
1397
|
+
mappings.push(new Mapping('bucket'));
|
|
1398
|
+
mappings.push(new Mapping('generation'));
|
|
1399
|
+
mappings.push(new Mapping('metageneration'));
|
|
1400
|
+
mappings.push(new Mapping('name', 'fullPath', true));
|
|
1401
|
+
function mappingsXformPath(_metadata, fullPath) {
|
|
1402
|
+
return xformPath(fullPath);
|
|
1403
|
+
}
|
|
1404
|
+
const nameMapping = new Mapping('name');
|
|
1405
|
+
nameMapping.xform = mappingsXformPath;
|
|
1406
|
+
mappings.push(nameMapping);
|
|
1407
|
+
/**
|
|
1408
|
+
* Coerces the second param to a number, if it is defined.
|
|
1409
|
+
*/
|
|
1410
|
+
function xformSize(_metadata, size) {
|
|
1411
|
+
if (size !== undefined) {
|
|
1412
|
+
return Number(size);
|
|
1413
|
+
}
|
|
1414
|
+
else {
|
|
1415
|
+
return size;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
const sizeMapping = new Mapping('size');
|
|
1419
|
+
sizeMapping.xform = xformSize;
|
|
1420
|
+
mappings.push(sizeMapping);
|
|
1421
|
+
mappings.push(new Mapping('timeCreated'));
|
|
1422
|
+
mappings.push(new Mapping('updated'));
|
|
1423
|
+
mappings.push(new Mapping('md5Hash', null, true));
|
|
1424
|
+
mappings.push(new Mapping('cacheControl', null, true));
|
|
1425
|
+
mappings.push(new Mapping('contentDisposition', null, true));
|
|
1426
|
+
mappings.push(new Mapping('contentEncoding', null, true));
|
|
1427
|
+
mappings.push(new Mapping('contentLanguage', null, true));
|
|
1428
|
+
mappings.push(new Mapping('contentType', null, true));
|
|
1429
|
+
mappings.push(new Mapping('metadata', 'customMetadata', true));
|
|
1430
|
+
mappings_ = mappings;
|
|
1431
|
+
return mappings_;
|
|
1432
|
+
}
|
|
1433
|
+
function addRef(metadata, service) {
|
|
1434
|
+
function generateRef() {
|
|
1435
|
+
const bucket = metadata['bucket'];
|
|
1436
|
+
const path = metadata['fullPath'];
|
|
1437
|
+
const loc = new Location(bucket, path);
|
|
1438
|
+
return service._makeStorageReference(loc);
|
|
1439
|
+
}
|
|
1440
|
+
Object.defineProperty(metadata, 'ref', { get: generateRef });
|
|
1441
|
+
}
|
|
1442
|
+
function fromResource(service, resource, mappings) {
|
|
1443
|
+
const metadata = {};
|
|
1444
|
+
metadata['type'] = 'file';
|
|
1445
|
+
const len = mappings.length;
|
|
1446
|
+
for (let i = 0; i < len; i++) {
|
|
1447
|
+
const mapping = mappings[i];
|
|
1448
|
+
metadata[mapping.local] = mapping.xform(metadata, resource[mapping.server]);
|
|
1449
|
+
}
|
|
1450
|
+
addRef(metadata, service);
|
|
1451
|
+
return metadata;
|
|
1452
|
+
}
|
|
1453
|
+
function fromResourceString(service, resourceString, mappings) {
|
|
1454
|
+
const obj = jsonObjectOrNull(resourceString);
|
|
1455
|
+
if (obj === null) {
|
|
1456
|
+
return null;
|
|
1457
|
+
}
|
|
1458
|
+
const resource = obj;
|
|
1459
|
+
return fromResource(service, resource, mappings);
|
|
1460
|
+
}
|
|
1461
|
+
function downloadUrlFromResourceString(metadata, resourceString, host, protocol) {
|
|
1462
|
+
const obj = jsonObjectOrNull(resourceString);
|
|
1463
|
+
if (obj === null) {
|
|
1464
|
+
return null;
|
|
1465
|
+
}
|
|
1466
|
+
if (!isString(obj['downloadTokens'])) {
|
|
1467
|
+
// This can happen if objects are uploaded through GCS and retrieved
|
|
1468
|
+
// through list, so we don't want to throw an Error.
|
|
1469
|
+
return null;
|
|
1470
|
+
}
|
|
1471
|
+
const tokens = obj['downloadTokens'];
|
|
1472
|
+
if (tokens.length === 0) {
|
|
1473
|
+
return null;
|
|
1474
|
+
}
|
|
1475
|
+
const encode = encodeURIComponent;
|
|
1476
|
+
const tokensList = tokens.split(',');
|
|
1477
|
+
const urls = tokensList.map((token) => {
|
|
1478
|
+
const bucket = metadata['bucket'];
|
|
1479
|
+
const path = metadata['fullPath'];
|
|
1480
|
+
const urlPart = '/b/' + encode(bucket) + '/o/' + encode(path);
|
|
1481
|
+
const base = makeUrl(urlPart, host, protocol);
|
|
1482
|
+
const queryString = makeQueryString({
|
|
1483
|
+
alt: 'media',
|
|
1484
|
+
token
|
|
1485
|
+
});
|
|
1486
|
+
return base + queryString;
|
|
1487
|
+
});
|
|
1488
|
+
return urls[0];
|
|
1489
|
+
}
|
|
1490
|
+
function toResourceString(metadata, mappings) {
|
|
1491
|
+
const resource = {};
|
|
1492
|
+
const len = mappings.length;
|
|
1493
|
+
for (let i = 0; i < len; i++) {
|
|
1494
|
+
const mapping = mappings[i];
|
|
1495
|
+
if (mapping.writable) {
|
|
1496
|
+
resource[mapping.server] = metadata[mapping.local];
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
return JSON.stringify(resource);
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
/**
|
|
1503
|
+
* @license
|
|
1504
|
+
* Copyright 2019 Google LLC
|
|
1505
|
+
*
|
|
1506
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1507
|
+
* you may not use this file except in compliance with the License.
|
|
1508
|
+
* You may obtain a copy of the License at
|
|
1509
|
+
*
|
|
1510
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1511
|
+
*
|
|
1512
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1513
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1514
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1515
|
+
* See the License for the specific language governing permissions and
|
|
1516
|
+
* limitations under the License.
|
|
1517
|
+
*/
|
|
1518
|
+
const PREFIXES_KEY = 'prefixes';
|
|
1519
|
+
const ITEMS_KEY = 'items';
|
|
1520
|
+
function fromBackendResponse(service, bucket, resource) {
|
|
1521
|
+
const listResult = {
|
|
1522
|
+
prefixes: [],
|
|
1523
|
+
items: [],
|
|
1524
|
+
nextPageToken: resource['nextPageToken']
|
|
1525
|
+
};
|
|
1526
|
+
if (resource[PREFIXES_KEY]) {
|
|
1527
|
+
for (const path of resource[PREFIXES_KEY]) {
|
|
1528
|
+
const pathWithoutTrailingSlash = path.replace(/\/$/, '');
|
|
1529
|
+
const reference = service._makeStorageReference(new Location(bucket, pathWithoutTrailingSlash));
|
|
1530
|
+
listResult.prefixes.push(reference);
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
if (resource[ITEMS_KEY]) {
|
|
1534
|
+
for (const item of resource[ITEMS_KEY]) {
|
|
1535
|
+
const reference = service._makeStorageReference(new Location(bucket, item['name']));
|
|
1536
|
+
listResult.items.push(reference);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
return listResult;
|
|
1540
|
+
}
|
|
1541
|
+
function fromResponseString(service, bucket, resourceString) {
|
|
1542
|
+
const obj = jsonObjectOrNull(resourceString);
|
|
1543
|
+
if (obj === null) {
|
|
1544
|
+
return null;
|
|
1545
|
+
}
|
|
1546
|
+
const resource = obj;
|
|
1547
|
+
return fromBackendResponse(service, bucket, resource);
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
/**
|
|
1551
|
+
* Contains a fully specified request.
|
|
1552
|
+
*
|
|
1553
|
+
* @param I - the type of the backend's network response.
|
|
1554
|
+
* @param O - the output response type used by the rest of the SDK.
|
|
1555
|
+
*/
|
|
1556
|
+
class RequestInfo {
|
|
1557
|
+
constructor(url, method,
|
|
1558
|
+
/**
|
|
1559
|
+
* Returns the value with which to resolve the request's promise. Only called
|
|
1560
|
+
* if the request is successful. Throw from this function to reject the
|
|
1561
|
+
* returned Request's promise with the thrown error.
|
|
1562
|
+
* Note: The XhrIo passed to this function may be reused after this callback
|
|
1563
|
+
* returns. Do not keep a reference to it in any way.
|
|
1564
|
+
*/
|
|
1565
|
+
handler, timeout) {
|
|
1566
|
+
this.url = url;
|
|
1567
|
+
this.method = method;
|
|
1568
|
+
this.handler = handler;
|
|
1569
|
+
this.timeout = timeout;
|
|
1570
|
+
this.urlParams = {};
|
|
1571
|
+
this.headers = {};
|
|
1572
|
+
this.body = null;
|
|
1573
|
+
this.errorHandler = null;
|
|
1574
|
+
/**
|
|
1575
|
+
* Called with the current number of bytes uploaded and total size (-1 if not
|
|
1576
|
+
* computable) of the request body (i.e. used to report upload progress).
|
|
1577
|
+
*/
|
|
1578
|
+
this.progressCallback = null;
|
|
1579
|
+
this.successCodes = [200];
|
|
1580
|
+
this.additionalRetryCodes = [];
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
/**
|
|
1585
|
+
* @license
|
|
1586
|
+
* Copyright 2017 Google LLC
|
|
1587
|
+
*
|
|
1588
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1589
|
+
* you may not use this file except in compliance with the License.
|
|
1590
|
+
* You may obtain a copy of the License at
|
|
1591
|
+
*
|
|
1592
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1593
|
+
*
|
|
1594
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1595
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1596
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1597
|
+
* See the License for the specific language governing permissions and
|
|
1598
|
+
* limitations under the License.
|
|
1599
|
+
*/
|
|
1600
|
+
/**
|
|
1601
|
+
* Throws the UNKNOWN StorageError if cndn is false.
|
|
1602
|
+
*/
|
|
1603
|
+
function handlerCheck(cndn) {
|
|
1604
|
+
if (!cndn) {
|
|
1605
|
+
throw unknown();
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
function metadataHandler(service, mappings) {
|
|
1609
|
+
function handler(xhr, text) {
|
|
1610
|
+
const metadata = fromResourceString(service, text, mappings);
|
|
1611
|
+
handlerCheck(metadata !== null);
|
|
1612
|
+
return metadata;
|
|
1613
|
+
}
|
|
1614
|
+
return handler;
|
|
1615
|
+
}
|
|
1616
|
+
function listHandler(service, bucket) {
|
|
1617
|
+
function handler(xhr, text) {
|
|
1618
|
+
const listResult = fromResponseString(service, bucket, text);
|
|
1619
|
+
handlerCheck(listResult !== null);
|
|
1620
|
+
return listResult;
|
|
1621
|
+
}
|
|
1622
|
+
return handler;
|
|
1623
|
+
}
|
|
1624
|
+
function downloadUrlHandler(service, mappings) {
|
|
1625
|
+
function handler(xhr, text) {
|
|
1626
|
+
const metadata = fromResourceString(service, text, mappings);
|
|
1627
|
+
handlerCheck(metadata !== null);
|
|
1628
|
+
return downloadUrlFromResourceString(metadata, text, service.host, service._protocol);
|
|
1629
|
+
}
|
|
1630
|
+
return handler;
|
|
1631
|
+
}
|
|
1632
|
+
function sharedErrorHandler(location) {
|
|
1633
|
+
function errorHandler(xhr, err) {
|
|
1634
|
+
let newErr;
|
|
1635
|
+
if (xhr.getStatus() === 401) {
|
|
1636
|
+
if (
|
|
1637
|
+
// This exact message string is the only consistent part of the
|
|
1638
|
+
// server's error response that identifies it as an App Check error.
|
|
1639
|
+
xhr.getErrorText().includes('Firebase App Check token is invalid')) {
|
|
1640
|
+
newErr = unauthorizedApp();
|
|
1641
|
+
}
|
|
1642
|
+
else {
|
|
1643
|
+
newErr = unauthenticated();
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
else {
|
|
1647
|
+
if (xhr.getStatus() === 402) {
|
|
1648
|
+
newErr = quotaExceeded(location.bucket);
|
|
1649
|
+
}
|
|
1650
|
+
else {
|
|
1651
|
+
if (xhr.getStatus() === 403) {
|
|
1652
|
+
newErr = unauthorized(location.path);
|
|
1653
|
+
}
|
|
1654
|
+
else {
|
|
1655
|
+
newErr = err;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
newErr.status = xhr.getStatus();
|
|
1660
|
+
newErr.serverResponse = err.serverResponse;
|
|
1661
|
+
return newErr;
|
|
1662
|
+
}
|
|
1663
|
+
return errorHandler;
|
|
1664
|
+
}
|
|
1665
|
+
function objectErrorHandler(location) {
|
|
1666
|
+
const shared = sharedErrorHandler(location);
|
|
1667
|
+
function errorHandler(xhr, err) {
|
|
1668
|
+
let newErr = shared(xhr, err);
|
|
1669
|
+
if (xhr.getStatus() === 404) {
|
|
1670
|
+
newErr = objectNotFound(location.path);
|
|
1671
|
+
}
|
|
1672
|
+
newErr.serverResponse = err.serverResponse;
|
|
1673
|
+
return newErr;
|
|
1674
|
+
}
|
|
1675
|
+
return errorHandler;
|
|
1676
|
+
}
|
|
1677
|
+
function getMetadata$2(service, location, mappings) {
|
|
1678
|
+
const urlPart = location.fullServerUrl();
|
|
1679
|
+
const url = makeUrl(urlPart, service.host, service._protocol);
|
|
1680
|
+
const method = 'GET';
|
|
1681
|
+
const timeout = service.maxOperationRetryTime;
|
|
1682
|
+
const requestInfo = new RequestInfo(url, method, metadataHandler(service, mappings), timeout);
|
|
1683
|
+
requestInfo.errorHandler = objectErrorHandler(location);
|
|
1684
|
+
return requestInfo;
|
|
1685
|
+
}
|
|
1686
|
+
function list$2(service, location, delimiter, pageToken, maxResults) {
|
|
1687
|
+
const urlParams = {};
|
|
1688
|
+
if (location.isRoot) {
|
|
1689
|
+
urlParams['prefix'] = '';
|
|
1690
|
+
}
|
|
1691
|
+
else {
|
|
1692
|
+
urlParams['prefix'] = location.path + '/';
|
|
1693
|
+
}
|
|
1694
|
+
if (delimiter && delimiter.length > 0) {
|
|
1695
|
+
urlParams['delimiter'] = delimiter;
|
|
1696
|
+
}
|
|
1697
|
+
if (pageToken) {
|
|
1698
|
+
urlParams['pageToken'] = pageToken;
|
|
1699
|
+
}
|
|
1700
|
+
if (maxResults) {
|
|
1701
|
+
urlParams['maxResults'] = maxResults;
|
|
1702
|
+
}
|
|
1703
|
+
const urlPart = location.bucketOnlyServerUrl();
|
|
1704
|
+
const url = makeUrl(urlPart, service.host, service._protocol);
|
|
1705
|
+
const method = 'GET';
|
|
1706
|
+
const timeout = service.maxOperationRetryTime;
|
|
1707
|
+
const requestInfo = new RequestInfo(url, method, listHandler(service, location.bucket), timeout);
|
|
1708
|
+
requestInfo.urlParams = urlParams;
|
|
1709
|
+
requestInfo.errorHandler = sharedErrorHandler(location);
|
|
1710
|
+
return requestInfo;
|
|
1711
|
+
}
|
|
1712
|
+
function getBytes$1(service, location, maxDownloadSizeBytes) {
|
|
1713
|
+
const urlPart = location.fullServerUrl();
|
|
1714
|
+
const url = makeUrl(urlPart, service.host, service._protocol) + '?alt=media';
|
|
1715
|
+
const method = 'GET';
|
|
1716
|
+
const timeout = service.maxOperationRetryTime;
|
|
1717
|
+
const requestInfo = new RequestInfo(url, method, (_, data) => data, timeout);
|
|
1718
|
+
requestInfo.errorHandler = objectErrorHandler(location);
|
|
1719
|
+
if (maxDownloadSizeBytes !== undefined) {
|
|
1720
|
+
requestInfo.headers['Range'] = `bytes=0-${maxDownloadSizeBytes}`;
|
|
1721
|
+
requestInfo.successCodes = [200 /* OK */, 206 /* Partial Content */];
|
|
1722
|
+
}
|
|
1723
|
+
return requestInfo;
|
|
1724
|
+
}
|
|
1725
|
+
function getDownloadUrl(service, location, mappings) {
|
|
1726
|
+
const urlPart = location.fullServerUrl();
|
|
1727
|
+
const url = makeUrl(urlPart, service.host, service._protocol);
|
|
1728
|
+
const method = 'GET';
|
|
1729
|
+
const timeout = service.maxOperationRetryTime;
|
|
1730
|
+
const requestInfo = new RequestInfo(url, method, downloadUrlHandler(service, mappings), timeout);
|
|
1731
|
+
requestInfo.errorHandler = objectErrorHandler(location);
|
|
1732
|
+
return requestInfo;
|
|
1733
|
+
}
|
|
1734
|
+
function updateMetadata$2(service, location, metadata, mappings) {
|
|
1735
|
+
const urlPart = location.fullServerUrl();
|
|
1736
|
+
const url = makeUrl(urlPart, service.host, service._protocol);
|
|
1737
|
+
const method = 'PATCH';
|
|
1738
|
+
const body = toResourceString(metadata, mappings);
|
|
1739
|
+
const headers = { 'Content-Type': 'application/json; charset=utf-8' };
|
|
1740
|
+
const timeout = service.maxOperationRetryTime;
|
|
1741
|
+
const requestInfo = new RequestInfo(url, method, metadataHandler(service, mappings), timeout);
|
|
1742
|
+
requestInfo.headers = headers;
|
|
1743
|
+
requestInfo.body = body;
|
|
1744
|
+
requestInfo.errorHandler = objectErrorHandler(location);
|
|
1745
|
+
return requestInfo;
|
|
1746
|
+
}
|
|
1747
|
+
function deleteObject$2(service, location) {
|
|
1748
|
+
const urlPart = location.fullServerUrl();
|
|
1749
|
+
const url = makeUrl(urlPart, service.host, service._protocol);
|
|
1750
|
+
const method = 'DELETE';
|
|
1751
|
+
const timeout = service.maxOperationRetryTime;
|
|
1752
|
+
function handler(_xhr, _text) { }
|
|
1753
|
+
const requestInfo = new RequestInfo(url, method, handler, timeout);
|
|
1754
|
+
requestInfo.successCodes = [200, 204];
|
|
1755
|
+
requestInfo.errorHandler = objectErrorHandler(location);
|
|
1756
|
+
return requestInfo;
|
|
1757
|
+
}
|
|
1758
|
+
function determineContentType_(metadata, blob) {
|
|
1759
|
+
return ((metadata && metadata['contentType']) ||
|
|
1760
|
+
(blob && blob.type()) ||
|
|
1761
|
+
'application/octet-stream');
|
|
1762
|
+
}
|
|
1763
|
+
function metadataForUpload_(location, blob, metadata) {
|
|
1764
|
+
const metadataClone = Object.assign({}, metadata);
|
|
1765
|
+
metadataClone['fullPath'] = location.path;
|
|
1766
|
+
metadataClone['size'] = blob.size();
|
|
1767
|
+
if (!metadataClone['contentType']) {
|
|
1768
|
+
metadataClone['contentType'] = determineContentType_(null, blob);
|
|
1769
|
+
}
|
|
1770
|
+
return metadataClone;
|
|
1771
|
+
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Prepare RequestInfo for uploads as Content-Type: multipart.
|
|
1774
|
+
*/
|
|
1775
|
+
function multipartUpload(service, location, mappings, blob, metadata) {
|
|
1776
|
+
const urlPart = location.bucketOnlyServerUrl();
|
|
1777
|
+
const headers = {
|
|
1778
|
+
'X-Goog-Upload-Protocol': 'multipart'
|
|
1779
|
+
};
|
|
1780
|
+
function genBoundary() {
|
|
1781
|
+
let str = '';
|
|
1782
|
+
for (let i = 0; i < 2; i++) {
|
|
1783
|
+
str = str + Math.random().toString().slice(2);
|
|
1784
|
+
}
|
|
1785
|
+
return str;
|
|
1786
|
+
}
|
|
1787
|
+
const boundary = genBoundary();
|
|
1788
|
+
headers['Content-Type'] = 'multipart/related; boundary=' + boundary;
|
|
1789
|
+
const metadata_ = metadataForUpload_(location, blob, metadata);
|
|
1790
|
+
const metadataString = toResourceString(metadata_, mappings);
|
|
1791
|
+
const preBlobPart = '--' +
|
|
1792
|
+
boundary +
|
|
1793
|
+
'\r\n' +
|
|
1794
|
+
'Content-Type: application/json; charset=utf-8\r\n\r\n' +
|
|
1795
|
+
metadataString +
|
|
1796
|
+
'\r\n--' +
|
|
1797
|
+
boundary +
|
|
1798
|
+
'\r\n' +
|
|
1799
|
+
'Content-Type: ' +
|
|
1800
|
+
metadata_['contentType'] +
|
|
1801
|
+
'\r\n\r\n';
|
|
1802
|
+
const postBlobPart = '\r\n--' + boundary + '--';
|
|
1803
|
+
const body = FbsBlob.getBlob(preBlobPart, blob, postBlobPart);
|
|
1804
|
+
if (body === null) {
|
|
1805
|
+
throw cannotSliceBlob();
|
|
1806
|
+
}
|
|
1807
|
+
const urlParams = { name: metadata_['fullPath'] };
|
|
1808
|
+
const url = makeUrl(urlPart, service.host, service._protocol);
|
|
1809
|
+
const method = 'POST';
|
|
1810
|
+
const timeout = service.maxUploadRetryTime;
|
|
1811
|
+
const requestInfo = new RequestInfo(url, method, metadataHandler(service, mappings), timeout);
|
|
1812
|
+
requestInfo.urlParams = urlParams;
|
|
1813
|
+
requestInfo.headers = headers;
|
|
1814
|
+
requestInfo.body = body.uploadData();
|
|
1815
|
+
requestInfo.errorHandler = sharedErrorHandler(location);
|
|
1816
|
+
return requestInfo;
|
|
1817
|
+
}
|
|
1818
|
+
/**
|
|
1819
|
+
* @param current The number of bytes that have been uploaded so far.
|
|
1820
|
+
* @param total The total number of bytes in the upload.
|
|
1821
|
+
* @param opt_finalized True if the server has finished the upload.
|
|
1822
|
+
* @param opt_metadata The upload metadata, should
|
|
1823
|
+
* only be passed if opt_finalized is true.
|
|
1824
|
+
*/
|
|
1825
|
+
class ResumableUploadStatus {
|
|
1826
|
+
constructor(current, total, finalized, metadata) {
|
|
1827
|
+
this.current = current;
|
|
1828
|
+
this.total = total;
|
|
1829
|
+
this.finalized = !!finalized;
|
|
1830
|
+
this.metadata = metadata || null;
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
function checkResumeHeader_(xhr, allowed) {
|
|
1834
|
+
let status = null;
|
|
1835
|
+
try {
|
|
1836
|
+
status = xhr.getResponseHeader('X-Goog-Upload-Status');
|
|
1837
|
+
}
|
|
1838
|
+
catch (e) {
|
|
1839
|
+
handlerCheck(false);
|
|
1840
|
+
}
|
|
1841
|
+
const allowedStatus = allowed || ['active'];
|
|
1842
|
+
handlerCheck(!!status && allowedStatus.indexOf(status) !== -1);
|
|
1843
|
+
return status;
|
|
1844
|
+
}
|
|
1845
|
+
function createResumableUpload(service, location, mappings, blob, metadata) {
|
|
1846
|
+
const urlPart = location.bucketOnlyServerUrl();
|
|
1847
|
+
const metadataForUpload = metadataForUpload_(location, blob, metadata);
|
|
1848
|
+
const urlParams = { name: metadataForUpload['fullPath'] };
|
|
1849
|
+
const url = makeUrl(urlPart, service.host, service._protocol);
|
|
1850
|
+
const method = 'POST';
|
|
1851
|
+
const headers = {
|
|
1852
|
+
'X-Goog-Upload-Protocol': 'resumable',
|
|
1853
|
+
'X-Goog-Upload-Command': 'start',
|
|
1854
|
+
'X-Goog-Upload-Header-Content-Length': `${blob.size()}`,
|
|
1855
|
+
'X-Goog-Upload-Header-Content-Type': metadataForUpload['contentType'],
|
|
1856
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
1857
|
+
};
|
|
1858
|
+
const body = toResourceString(metadataForUpload, mappings);
|
|
1859
|
+
const timeout = service.maxUploadRetryTime;
|
|
1860
|
+
function handler(xhr) {
|
|
1861
|
+
checkResumeHeader_(xhr);
|
|
1862
|
+
let url;
|
|
1863
|
+
try {
|
|
1864
|
+
url = xhr.getResponseHeader('X-Goog-Upload-URL');
|
|
1865
|
+
}
|
|
1866
|
+
catch (e) {
|
|
1867
|
+
handlerCheck(false);
|
|
1868
|
+
}
|
|
1869
|
+
handlerCheck(isString(url));
|
|
1870
|
+
return url;
|
|
1871
|
+
}
|
|
1872
|
+
const requestInfo = new RequestInfo(url, method, handler, timeout);
|
|
1873
|
+
requestInfo.urlParams = urlParams;
|
|
1874
|
+
requestInfo.headers = headers;
|
|
1875
|
+
requestInfo.body = body;
|
|
1876
|
+
requestInfo.errorHandler = sharedErrorHandler(location);
|
|
1877
|
+
return requestInfo;
|
|
1878
|
+
}
|
|
1879
|
+
/**
|
|
1880
|
+
* @param url From a call to fbs.requests.createResumableUpload.
|
|
1881
|
+
*/
|
|
1882
|
+
function getResumableUploadStatus(service, location, url, blob) {
|
|
1883
|
+
const headers = { 'X-Goog-Upload-Command': 'query' };
|
|
1884
|
+
function handler(xhr) {
|
|
1885
|
+
const status = checkResumeHeader_(xhr, ['active', 'final']);
|
|
1886
|
+
let sizeString = null;
|
|
1887
|
+
try {
|
|
1888
|
+
sizeString = xhr.getResponseHeader('X-Goog-Upload-Size-Received');
|
|
1889
|
+
}
|
|
1890
|
+
catch (e) {
|
|
1891
|
+
handlerCheck(false);
|
|
1892
|
+
}
|
|
1893
|
+
if (!sizeString) {
|
|
1894
|
+
// null or empty string
|
|
1895
|
+
handlerCheck(false);
|
|
1896
|
+
}
|
|
1897
|
+
const size = Number(sizeString);
|
|
1898
|
+
handlerCheck(!isNaN(size));
|
|
1899
|
+
return new ResumableUploadStatus(size, blob.size(), status === 'final');
|
|
1900
|
+
}
|
|
1901
|
+
const method = 'POST';
|
|
1902
|
+
const timeout = service.maxUploadRetryTime;
|
|
1903
|
+
const requestInfo = new RequestInfo(url, method, handler, timeout);
|
|
1904
|
+
requestInfo.headers = headers;
|
|
1905
|
+
requestInfo.errorHandler = sharedErrorHandler(location);
|
|
1906
|
+
return requestInfo;
|
|
1907
|
+
}
|
|
1908
|
+
/**
|
|
1909
|
+
* Any uploads via the resumable upload API must transfer a number of bytes
|
|
1910
|
+
* that is a multiple of this number.
|
|
1911
|
+
*/
|
|
1912
|
+
const RESUMABLE_UPLOAD_CHUNK_SIZE = 256 * 1024;
|
|
1913
|
+
/**
|
|
1914
|
+
* @param url From a call to fbs.requests.createResumableUpload.
|
|
1915
|
+
* @param chunkSize Number of bytes to upload.
|
|
1916
|
+
* @param status The previous status.
|
|
1917
|
+
* If not passed or null, we start from the beginning.
|
|
1918
|
+
* @throws fbs.Error If the upload is already complete, the passed in status
|
|
1919
|
+
* has a final size inconsistent with the blob, or the blob cannot be sliced
|
|
1920
|
+
* for upload.
|
|
1921
|
+
*/
|
|
1922
|
+
function continueResumableUpload(location, service, url, blob, chunkSize, mappings, status, progressCallback) {
|
|
1923
|
+
// TODO(andysoto): standardize on internal asserts
|
|
1924
|
+
// assert(!(opt_status && opt_status.finalized));
|
|
1925
|
+
const status_ = new ResumableUploadStatus(0, 0);
|
|
1926
|
+
if (status) {
|
|
1927
|
+
status_.current = status.current;
|
|
1928
|
+
status_.total = status.total;
|
|
1929
|
+
}
|
|
1930
|
+
else {
|
|
1931
|
+
status_.current = 0;
|
|
1932
|
+
status_.total = blob.size();
|
|
1933
|
+
}
|
|
1934
|
+
if (blob.size() !== status_.total) {
|
|
1935
|
+
throw serverFileWrongSize();
|
|
1936
|
+
}
|
|
1937
|
+
const bytesLeft = status_.total - status_.current;
|
|
1938
|
+
let bytesToUpload = bytesLeft;
|
|
1939
|
+
if (chunkSize > 0) {
|
|
1940
|
+
bytesToUpload = Math.min(bytesToUpload, chunkSize);
|
|
1941
|
+
}
|
|
1942
|
+
const startByte = status_.current;
|
|
1943
|
+
const endByte = startByte + bytesToUpload;
|
|
1944
|
+
let uploadCommand = '';
|
|
1945
|
+
if (bytesToUpload === 0) {
|
|
1946
|
+
uploadCommand = 'finalize';
|
|
1947
|
+
}
|
|
1948
|
+
else if (bytesLeft === bytesToUpload) {
|
|
1949
|
+
uploadCommand = 'upload, finalize';
|
|
1950
|
+
}
|
|
1951
|
+
else {
|
|
1952
|
+
uploadCommand = 'upload';
|
|
1953
|
+
}
|
|
1954
|
+
const headers = {
|
|
1955
|
+
'X-Goog-Upload-Command': uploadCommand,
|
|
1956
|
+
'X-Goog-Upload-Offset': `${status_.current}`
|
|
1957
|
+
};
|
|
1958
|
+
const body = blob.slice(startByte, endByte);
|
|
1959
|
+
if (body === null) {
|
|
1960
|
+
throw cannotSliceBlob();
|
|
1961
|
+
}
|
|
1962
|
+
function handler(xhr, text) {
|
|
1963
|
+
// TODO(andysoto): Verify the MD5 of each uploaded range:
|
|
1964
|
+
// the 'x-range-md5' header comes back with status code 308 responses.
|
|
1965
|
+
// We'll only be able to bail out though, because you can't re-upload a
|
|
1966
|
+
// range that you previously uploaded.
|
|
1967
|
+
const uploadStatus = checkResumeHeader_(xhr, ['active', 'final']);
|
|
1968
|
+
const newCurrent = status_.current + bytesToUpload;
|
|
1969
|
+
const size = blob.size();
|
|
1970
|
+
let metadata;
|
|
1971
|
+
if (uploadStatus === 'final') {
|
|
1972
|
+
metadata = metadataHandler(service, mappings)(xhr, text);
|
|
1973
|
+
}
|
|
1974
|
+
else {
|
|
1975
|
+
metadata = null;
|
|
1976
|
+
}
|
|
1977
|
+
return new ResumableUploadStatus(newCurrent, size, uploadStatus === 'final', metadata);
|
|
1978
|
+
}
|
|
1979
|
+
const method = 'POST';
|
|
1980
|
+
const timeout = service.maxUploadRetryTime;
|
|
1981
|
+
const requestInfo = new RequestInfo(url, method, handler, timeout);
|
|
1982
|
+
requestInfo.headers = headers;
|
|
1983
|
+
requestInfo.body = body.uploadData();
|
|
1984
|
+
requestInfo.progressCallback = progressCallback || null;
|
|
1985
|
+
requestInfo.errorHandler = sharedErrorHandler(location);
|
|
1986
|
+
return requestInfo;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
/**
|
|
1990
|
+
* @license
|
|
1991
|
+
* Copyright 2017 Google LLC
|
|
1992
|
+
*
|
|
1993
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1994
|
+
* you may not use this file except in compliance with the License.
|
|
1995
|
+
* You may obtain a copy of the License at
|
|
1996
|
+
*
|
|
1997
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1998
|
+
*
|
|
1999
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2000
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2001
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2002
|
+
* See the License for the specific language governing permissions and
|
|
2003
|
+
* limitations under the License.
|
|
2004
|
+
*/
|
|
2005
|
+
/**
|
|
2006
|
+
* An event that is triggered on a task.
|
|
2007
|
+
* @internal
|
|
2008
|
+
*/
|
|
2009
|
+
const TaskEvent = {
|
|
2010
|
+
/**
|
|
2011
|
+
* For this event,
|
|
2012
|
+
* <ul>
|
|
2013
|
+
* <li>The `next` function is triggered on progress updates and when the
|
|
2014
|
+
* task is paused/resumed with an `UploadTaskSnapshot` as the first
|
|
2015
|
+
* argument.</li>
|
|
2016
|
+
* <li>The `error` function is triggered if the upload is canceled or fails
|
|
2017
|
+
* for another reason.</li>
|
|
2018
|
+
* <li>The `complete` function is triggered if the upload completes
|
|
2019
|
+
* successfully.</li>
|
|
2020
|
+
* </ul>
|
|
2021
|
+
*/
|
|
2022
|
+
STATE_CHANGED: 'state_changed'
|
|
2023
|
+
};
|
|
2024
|
+
// type keys = keyof TaskState
|
|
2025
|
+
/**
|
|
2026
|
+
* Represents the current state of a running upload.
|
|
2027
|
+
* @internal
|
|
2028
|
+
*/
|
|
2029
|
+
const TaskState = {
|
|
2030
|
+
/** The task is currently transferring data. */
|
|
2031
|
+
RUNNING: 'running',
|
|
2032
|
+
/** The task was paused by the user. */
|
|
2033
|
+
PAUSED: 'paused',
|
|
2034
|
+
/** The task completed successfully. */
|
|
2035
|
+
SUCCESS: 'success',
|
|
2036
|
+
/** The task was canceled. */
|
|
2037
|
+
CANCELED: 'canceled',
|
|
2038
|
+
/** The task failed with an error. */
|
|
2039
|
+
ERROR: 'error'
|
|
2040
|
+
};
|
|
2041
|
+
function taskStateFromInternalTaskState(state) {
|
|
2042
|
+
switch (state) {
|
|
2043
|
+
case "running" /* InternalTaskState.RUNNING */:
|
|
2044
|
+
case "pausing" /* InternalTaskState.PAUSING */:
|
|
2045
|
+
case "canceling" /* InternalTaskState.CANCELING */:
|
|
2046
|
+
return TaskState.RUNNING;
|
|
2047
|
+
case "paused" /* InternalTaskState.PAUSED */:
|
|
2048
|
+
return TaskState.PAUSED;
|
|
2049
|
+
case "success" /* InternalTaskState.SUCCESS */:
|
|
2050
|
+
return TaskState.SUCCESS;
|
|
2051
|
+
case "canceled" /* InternalTaskState.CANCELED */:
|
|
2052
|
+
return TaskState.CANCELED;
|
|
2053
|
+
case "error" /* InternalTaskState.ERROR */:
|
|
2054
|
+
return TaskState.ERROR;
|
|
2055
|
+
default:
|
|
2056
|
+
// TODO(andysoto): assert(false);
|
|
2057
|
+
return TaskState.ERROR;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
/**
|
|
2062
|
+
* @license
|
|
2063
|
+
* Copyright 2017 Google LLC
|
|
2064
|
+
*
|
|
2065
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2066
|
+
* you may not use this file except in compliance with the License.
|
|
2067
|
+
* You may obtain a copy of the License at
|
|
2068
|
+
*
|
|
2069
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2070
|
+
*
|
|
2071
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2072
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2073
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2074
|
+
* See the License for the specific language governing permissions and
|
|
2075
|
+
* limitations under the License.
|
|
2076
|
+
*/
|
|
2077
|
+
class Observer {
|
|
2078
|
+
constructor(nextOrObserver, error, complete) {
|
|
2079
|
+
const asFunctions = isFunction(nextOrObserver) || error != null || complete != null;
|
|
2080
|
+
if (asFunctions) {
|
|
2081
|
+
this.next = nextOrObserver;
|
|
2082
|
+
this.error = error ?? undefined;
|
|
2083
|
+
this.complete = complete ?? undefined;
|
|
2084
|
+
}
|
|
2085
|
+
else {
|
|
2086
|
+
const observer = nextOrObserver;
|
|
2087
|
+
this.next = observer.next;
|
|
2088
|
+
this.error = observer.error;
|
|
2089
|
+
this.complete = observer.complete;
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
/**
|
|
2095
|
+
* @license
|
|
2096
|
+
* Copyright 2017 Google LLC
|
|
2097
|
+
*
|
|
2098
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2099
|
+
* you may not use this file except in compliance with the License.
|
|
2100
|
+
* You may obtain a copy of the License at
|
|
2101
|
+
*
|
|
2102
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2103
|
+
*
|
|
2104
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2105
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2106
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2107
|
+
* See the License for the specific language governing permissions and
|
|
2108
|
+
* limitations under the License.
|
|
2109
|
+
*/
|
|
2110
|
+
/**
|
|
2111
|
+
* Returns a function that invokes f with its arguments asynchronously as a
|
|
2112
|
+
* microtask, i.e. as soon as possible after the current script returns back
|
|
2113
|
+
* into browser code.
|
|
2114
|
+
*/
|
|
2115
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
2116
|
+
function async(f) {
|
|
2117
|
+
return (...argsToForward) => {
|
|
2118
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2119
|
+
Promise.resolve().then(() => f(...argsToForward));
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
/**
|
|
2124
|
+
* @license
|
|
2125
|
+
* Copyright 2021 Google LLC
|
|
2126
|
+
*
|
|
2127
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2128
|
+
* you may not use this file except in compliance with the License.
|
|
2129
|
+
* You may obtain a copy of the License at
|
|
2130
|
+
*
|
|
2131
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2132
|
+
*
|
|
2133
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2134
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2135
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2136
|
+
* See the License for the specific language governing permissions and
|
|
2137
|
+
* limitations under the License.
|
|
2138
|
+
*/
|
|
2139
|
+
/** An override for the text-based Connection. Used in tests. */
|
|
2140
|
+
let textFactoryOverride = null;
|
|
2141
|
+
/**
|
|
2142
|
+
* Network layer that works in Node.
|
|
2143
|
+
*
|
|
2144
|
+
* This network implementation should not be used in browsers as it does not
|
|
2145
|
+
* support progress updates.
|
|
2146
|
+
*/
|
|
2147
|
+
class FetchConnection {
|
|
2148
|
+
constructor() {
|
|
2149
|
+
this.errorText_ = '';
|
|
2150
|
+
this.sent_ = false;
|
|
2151
|
+
this.errorCode_ = ErrorCode.NO_ERROR;
|
|
2152
|
+
}
|
|
2153
|
+
async send(url, method, isUsingEmulator, body, headers) {
|
|
2154
|
+
if (this.sent_) {
|
|
2155
|
+
throw internalError('cannot .send() more than once');
|
|
2156
|
+
}
|
|
2157
|
+
this.sent_ = true;
|
|
2158
|
+
try {
|
|
2159
|
+
const response = await newFetch(url, method, isUsingEmulator, headers, body);
|
|
2160
|
+
this.headers_ = response.headers;
|
|
2161
|
+
this.statusCode_ = response.status;
|
|
2162
|
+
this.errorCode_ = ErrorCode.NO_ERROR;
|
|
2163
|
+
this.body_ = await response.arrayBuffer();
|
|
2164
|
+
}
|
|
2165
|
+
catch (e) {
|
|
2166
|
+
this.errorText_ = e?.message;
|
|
2167
|
+
// emulate XHR which sets status to 0 when encountering a network error
|
|
2168
|
+
this.statusCode_ = 0;
|
|
2169
|
+
this.errorCode_ = ErrorCode.NETWORK_ERROR;
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
getErrorCode() {
|
|
2173
|
+
if (this.errorCode_ === undefined) {
|
|
2174
|
+
throw internalError('cannot .getErrorCode() before receiving response');
|
|
2175
|
+
}
|
|
2176
|
+
return this.errorCode_;
|
|
2177
|
+
}
|
|
2178
|
+
getStatus() {
|
|
2179
|
+
if (this.statusCode_ === undefined) {
|
|
2180
|
+
throw internalError('cannot .getStatus() before receiving response');
|
|
2181
|
+
}
|
|
2182
|
+
return this.statusCode_;
|
|
2183
|
+
}
|
|
2184
|
+
getErrorText() {
|
|
2185
|
+
return this.errorText_;
|
|
2186
|
+
}
|
|
2187
|
+
abort() {
|
|
2188
|
+
// Not supported
|
|
2189
|
+
}
|
|
2190
|
+
getResponseHeader(header) {
|
|
2191
|
+
if (!this.headers_) {
|
|
2192
|
+
throw internalError('cannot .getResponseHeader() before receiving response');
|
|
2193
|
+
}
|
|
2194
|
+
return this.headers_.get(header);
|
|
2195
|
+
}
|
|
2196
|
+
addUploadProgressListener(listener) {
|
|
2197
|
+
// Not supported
|
|
2198
|
+
}
|
|
2199
|
+
removeUploadProgressListener(listener) {
|
|
2200
|
+
// Not supported
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
class FetchTextConnection extends FetchConnection {
|
|
2204
|
+
getResponse() {
|
|
2205
|
+
if (!this.body_) {
|
|
2206
|
+
throw internalError('cannot .getResponse() before receiving response');
|
|
2207
|
+
}
|
|
2208
|
+
return Buffer.from(this.body_).toString('utf-8');
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
function newTextConnection() {
|
|
2212
|
+
return textFactoryOverride
|
|
2213
|
+
? textFactoryOverride()
|
|
2214
|
+
: new FetchTextConnection();
|
|
2215
|
+
}
|
|
2216
|
+
class FetchBytesConnection extends FetchConnection {
|
|
2217
|
+
getResponse() {
|
|
2218
|
+
if (!this.body_) {
|
|
2219
|
+
throw internalError('cannot .getResponse() before sending');
|
|
2220
|
+
}
|
|
2221
|
+
return this.body_;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
function newBytesConnection() {
|
|
2225
|
+
return new FetchBytesConnection();
|
|
2226
|
+
}
|
|
2227
|
+
class FetchStreamConnection extends FetchConnection {
|
|
2228
|
+
constructor() {
|
|
2229
|
+
super(...arguments);
|
|
2230
|
+
this.stream_ = null;
|
|
2231
|
+
}
|
|
2232
|
+
async send(url, method, isUsingEmulator, body, headers) {
|
|
2233
|
+
if (this.sent_) {
|
|
2234
|
+
throw internalError('cannot .send() more than once');
|
|
2235
|
+
}
|
|
2236
|
+
this.sent_ = true;
|
|
2237
|
+
try {
|
|
2238
|
+
const response = await newFetch(url, method, isUsingEmulator, headers, body);
|
|
2239
|
+
this.headers_ = response.headers;
|
|
2240
|
+
this.statusCode_ = response.status;
|
|
2241
|
+
this.errorCode_ = ErrorCode.NO_ERROR;
|
|
2242
|
+
this.stream_ = response.body;
|
|
2243
|
+
}
|
|
2244
|
+
catch (e) {
|
|
2245
|
+
this.errorText_ = e?.message;
|
|
2246
|
+
// emulate XHR which sets status to 0 when encountering a network error
|
|
2247
|
+
this.statusCode_ = 0;
|
|
2248
|
+
this.errorCode_ = ErrorCode.NETWORK_ERROR;
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
getResponse() {
|
|
2252
|
+
if (!this.stream_) {
|
|
2253
|
+
throw internalError('cannot .getResponse() before sending');
|
|
2254
|
+
}
|
|
2255
|
+
return this.stream_;
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
function newFetch(url, method, isUsingEmulator, headers, body) {
|
|
2259
|
+
const fetchArgs = {
|
|
2260
|
+
method,
|
|
2261
|
+
headers: headers || {},
|
|
2262
|
+
body: body
|
|
2263
|
+
};
|
|
2264
|
+
if (util.isCloudWorkstation(url) && isUsingEmulator) {
|
|
2265
|
+
fetchArgs.credentials = 'include';
|
|
2266
|
+
}
|
|
2267
|
+
return fetch(url, fetchArgs);
|
|
2268
|
+
}
|
|
2269
|
+
function newStreamConnection() {
|
|
2270
|
+
return new FetchStreamConnection();
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
/**
|
|
2274
|
+
* @license
|
|
2275
|
+
* Copyright 2017 Google LLC
|
|
2276
|
+
*
|
|
2277
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2278
|
+
* you may not use this file except in compliance with the License.
|
|
2279
|
+
* You may obtain a copy of the License at
|
|
2280
|
+
*
|
|
2281
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2282
|
+
*
|
|
2283
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2284
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2285
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2286
|
+
* See the License for the specific language governing permissions and
|
|
2287
|
+
* limitations under the License.
|
|
2288
|
+
*/
|
|
2289
|
+
/**
|
|
2290
|
+
* Represents a blob being uploaded. Can be used to pause/resume/cancel the
|
|
2291
|
+
* upload and manage callbacks for various events.
|
|
2292
|
+
* @internal
|
|
2293
|
+
*/
|
|
2294
|
+
class UploadTask {
|
|
2295
|
+
isExponentialBackoffExpired() {
|
|
2296
|
+
return this.sleepTime > this.maxSleepTime;
|
|
2297
|
+
}
|
|
2298
|
+
/**
|
|
2299
|
+
* @param ref - The firebaseStorage.Reference object this task came
|
|
2300
|
+
* from, untyped to avoid cyclic dependencies.
|
|
2301
|
+
* @param blob - The blob to upload.
|
|
2302
|
+
*/
|
|
2303
|
+
constructor(ref, blob, metadata = null) {
|
|
2304
|
+
/**
|
|
2305
|
+
* Number of bytes transferred so far.
|
|
2306
|
+
*/
|
|
2307
|
+
this._transferred = 0;
|
|
2308
|
+
this._needToFetchStatus = false;
|
|
2309
|
+
this._needToFetchMetadata = false;
|
|
2310
|
+
this._observers = [];
|
|
2311
|
+
this._error = undefined;
|
|
2312
|
+
this._uploadUrl = undefined;
|
|
2313
|
+
this._request = undefined;
|
|
2314
|
+
this._chunkMultiplier = 1;
|
|
2315
|
+
this._resolve = undefined;
|
|
2316
|
+
this._reject = undefined;
|
|
2317
|
+
this._ref = ref;
|
|
2318
|
+
this._blob = blob;
|
|
2319
|
+
this._metadata = metadata;
|
|
2320
|
+
this._mappings = getMappings();
|
|
2321
|
+
this._resumable = this._shouldDoResumable(this._blob);
|
|
2322
|
+
this._state = "running" /* InternalTaskState.RUNNING */;
|
|
2323
|
+
this._errorHandler = error => {
|
|
2324
|
+
this._request = undefined;
|
|
2325
|
+
this._chunkMultiplier = 1;
|
|
2326
|
+
if (error._codeEquals(exports.StorageErrorCode.CANCELED)) {
|
|
2327
|
+
this._needToFetchStatus = true;
|
|
2328
|
+
this.completeTransitions_();
|
|
2329
|
+
}
|
|
2330
|
+
else {
|
|
2331
|
+
const backoffExpired = this.isExponentialBackoffExpired();
|
|
2332
|
+
if (isRetryStatusCode(error.status, [])) {
|
|
2333
|
+
if (backoffExpired) {
|
|
2334
|
+
error = retryLimitExceeded();
|
|
2335
|
+
}
|
|
2336
|
+
else {
|
|
2337
|
+
this.sleepTime = Math.max(this.sleepTime * 2, DEFAULT_MIN_SLEEP_TIME_MILLIS);
|
|
2338
|
+
this._needToFetchStatus = true;
|
|
2339
|
+
this.completeTransitions_();
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
this._error = error;
|
|
2344
|
+
this._transition("error" /* InternalTaskState.ERROR */);
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
this._metadataErrorHandler = error => {
|
|
2348
|
+
this._request = undefined;
|
|
2349
|
+
if (error._codeEquals(exports.StorageErrorCode.CANCELED)) {
|
|
2350
|
+
this.completeTransitions_();
|
|
2351
|
+
}
|
|
2352
|
+
else {
|
|
2353
|
+
this._error = error;
|
|
2354
|
+
this._transition("error" /* InternalTaskState.ERROR */);
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2357
|
+
this.sleepTime = 0;
|
|
2358
|
+
this.maxSleepTime = this._ref.storage.maxUploadRetryTime;
|
|
2359
|
+
this._promise = new Promise((resolve, reject) => {
|
|
2360
|
+
this._resolve = resolve;
|
|
2361
|
+
this._reject = reject;
|
|
2362
|
+
this._start();
|
|
2363
|
+
});
|
|
2364
|
+
// Prevent uncaught rejections on the internal promise from bubbling out
|
|
2365
|
+
// to the top level with a dummy handler.
|
|
2366
|
+
this._promise.then(null, () => { });
|
|
2367
|
+
}
|
|
2368
|
+
_makeProgressCallback() {
|
|
2369
|
+
const sizeBefore = this._transferred;
|
|
2370
|
+
return loaded => this._updateProgress(sizeBefore + loaded);
|
|
2371
|
+
}
|
|
2372
|
+
_shouldDoResumable(blob) {
|
|
2373
|
+
return blob.size() > 256 * 1024;
|
|
2374
|
+
}
|
|
2375
|
+
_start() {
|
|
2376
|
+
if (this._state !== "running" /* InternalTaskState.RUNNING */) {
|
|
2377
|
+
// This can happen if someone pauses us in a resume callback, for example.
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
if (this._request !== undefined) {
|
|
2381
|
+
return;
|
|
2382
|
+
}
|
|
2383
|
+
if (this._resumable) {
|
|
2384
|
+
if (this._uploadUrl === undefined) {
|
|
2385
|
+
this._createResumable();
|
|
2386
|
+
}
|
|
2387
|
+
else {
|
|
2388
|
+
if (this._needToFetchStatus) {
|
|
2389
|
+
this._fetchStatus();
|
|
2390
|
+
}
|
|
2391
|
+
else {
|
|
2392
|
+
if (this._needToFetchMetadata) {
|
|
2393
|
+
// Happens if we miss the metadata on upload completion.
|
|
2394
|
+
this._fetchMetadata();
|
|
2395
|
+
}
|
|
2396
|
+
else {
|
|
2397
|
+
this.pendingTimeout = setTimeout(() => {
|
|
2398
|
+
this.pendingTimeout = undefined;
|
|
2399
|
+
this._continueUpload();
|
|
2400
|
+
}, this.sleepTime);
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
else {
|
|
2406
|
+
this._oneShotUpload();
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
_resolveToken(callback) {
|
|
2410
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2411
|
+
Promise.all([
|
|
2412
|
+
this._ref.storage._getAuthToken(),
|
|
2413
|
+
this._ref.storage._getAppCheckToken()
|
|
2414
|
+
]).then(([authToken, appCheckToken]) => {
|
|
2415
|
+
switch (this._state) {
|
|
2416
|
+
case "running" /* InternalTaskState.RUNNING */:
|
|
2417
|
+
callback(authToken, appCheckToken);
|
|
2418
|
+
break;
|
|
2419
|
+
case "canceling" /* InternalTaskState.CANCELING */:
|
|
2420
|
+
this._transition("canceled" /* InternalTaskState.CANCELED */);
|
|
2421
|
+
break;
|
|
2422
|
+
case "pausing" /* InternalTaskState.PAUSING */:
|
|
2423
|
+
this._transition("paused" /* InternalTaskState.PAUSED */);
|
|
2424
|
+
break;
|
|
2425
|
+
}
|
|
2426
|
+
});
|
|
2427
|
+
}
|
|
2428
|
+
// TODO(andysoto): assert false
|
|
2429
|
+
_createResumable() {
|
|
2430
|
+
this._resolveToken((authToken, appCheckToken) => {
|
|
2431
|
+
const requestInfo = createResumableUpload(this._ref.storage, this._ref._location, this._mappings, this._blob, this._metadata);
|
|
2432
|
+
const createRequest = this._ref.storage._makeRequest(requestInfo, newTextConnection, authToken, appCheckToken);
|
|
2433
|
+
this._request = createRequest;
|
|
2434
|
+
createRequest.getPromise().then((url) => {
|
|
2435
|
+
this._request = undefined;
|
|
2436
|
+
this._uploadUrl = url;
|
|
2437
|
+
this._needToFetchStatus = false;
|
|
2438
|
+
this.completeTransitions_();
|
|
2439
|
+
}, this._errorHandler);
|
|
2440
|
+
});
|
|
2441
|
+
}
|
|
2442
|
+
_fetchStatus() {
|
|
2443
|
+
// TODO(andysoto): assert(this.uploadUrl_ !== null);
|
|
2444
|
+
const url = this._uploadUrl;
|
|
2445
|
+
this._resolveToken((authToken, appCheckToken) => {
|
|
2446
|
+
const requestInfo = getResumableUploadStatus(this._ref.storage, this._ref._location, url, this._blob);
|
|
2447
|
+
const statusRequest = this._ref.storage._makeRequest(requestInfo, newTextConnection, authToken, appCheckToken);
|
|
2448
|
+
this._request = statusRequest;
|
|
2449
|
+
statusRequest.getPromise().then(status => {
|
|
2450
|
+
status = status;
|
|
2451
|
+
this._request = undefined;
|
|
2452
|
+
this._updateProgress(status.current);
|
|
2453
|
+
this._needToFetchStatus = false;
|
|
2454
|
+
if (status.finalized) {
|
|
2455
|
+
this._needToFetchMetadata = true;
|
|
2456
|
+
}
|
|
2457
|
+
this.completeTransitions_();
|
|
2458
|
+
}, this._errorHandler);
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
_continueUpload() {
|
|
2462
|
+
const chunkSize = RESUMABLE_UPLOAD_CHUNK_SIZE * this._chunkMultiplier;
|
|
2463
|
+
const status = new ResumableUploadStatus(this._transferred, this._blob.size());
|
|
2464
|
+
// TODO(andysoto): assert(this.uploadUrl_ !== null);
|
|
2465
|
+
const url = this._uploadUrl;
|
|
2466
|
+
this._resolveToken((authToken, appCheckToken) => {
|
|
2467
|
+
let requestInfo;
|
|
2468
|
+
try {
|
|
2469
|
+
requestInfo = continueResumableUpload(this._ref._location, this._ref.storage, url, this._blob, chunkSize, this._mappings, status, this._makeProgressCallback());
|
|
2470
|
+
}
|
|
2471
|
+
catch (e) {
|
|
2472
|
+
this._error = e;
|
|
2473
|
+
this._transition("error" /* InternalTaskState.ERROR */);
|
|
2474
|
+
return;
|
|
2475
|
+
}
|
|
2476
|
+
const uploadRequest = this._ref.storage._makeRequest(requestInfo, newTextConnection, authToken, appCheckToken,
|
|
2477
|
+
/*retry=*/ false // Upload requests should not be retried as each retry should be preceded by another query request. Which is handled in this file.
|
|
2478
|
+
);
|
|
2479
|
+
this._request = uploadRequest;
|
|
2480
|
+
uploadRequest.getPromise().then((newStatus) => {
|
|
2481
|
+
this._increaseMultiplier();
|
|
2482
|
+
this._request = undefined;
|
|
2483
|
+
this._updateProgress(newStatus.current);
|
|
2484
|
+
if (newStatus.finalized) {
|
|
2485
|
+
this._metadata = newStatus.metadata;
|
|
2486
|
+
this._transition("success" /* InternalTaskState.SUCCESS */);
|
|
2487
|
+
}
|
|
2488
|
+
else {
|
|
2489
|
+
this.completeTransitions_();
|
|
2490
|
+
}
|
|
2491
|
+
}, this._errorHandler);
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2494
|
+
_increaseMultiplier() {
|
|
2495
|
+
const currentSize = RESUMABLE_UPLOAD_CHUNK_SIZE * this._chunkMultiplier;
|
|
2496
|
+
// Max chunk size is 32M.
|
|
2497
|
+
if (currentSize * 2 < 32 * 1024 * 1024) {
|
|
2498
|
+
this._chunkMultiplier *= 2;
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
_fetchMetadata() {
|
|
2502
|
+
this._resolveToken((authToken, appCheckToken) => {
|
|
2503
|
+
const requestInfo = getMetadata$2(this._ref.storage, this._ref._location, this._mappings);
|
|
2504
|
+
const metadataRequest = this._ref.storage._makeRequest(requestInfo, newTextConnection, authToken, appCheckToken);
|
|
2505
|
+
this._request = metadataRequest;
|
|
2506
|
+
metadataRequest.getPromise().then(metadata => {
|
|
2507
|
+
this._request = undefined;
|
|
2508
|
+
this._metadata = metadata;
|
|
2509
|
+
this._transition("success" /* InternalTaskState.SUCCESS */);
|
|
2510
|
+
}, this._metadataErrorHandler);
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
_oneShotUpload() {
|
|
2514
|
+
this._resolveToken((authToken, appCheckToken) => {
|
|
2515
|
+
const requestInfo = multipartUpload(this._ref.storage, this._ref._location, this._mappings, this._blob, this._metadata);
|
|
2516
|
+
const multipartRequest = this._ref.storage._makeRequest(requestInfo, newTextConnection, authToken, appCheckToken);
|
|
2517
|
+
this._request = multipartRequest;
|
|
2518
|
+
multipartRequest.getPromise().then(metadata => {
|
|
2519
|
+
this._request = undefined;
|
|
2520
|
+
this._metadata = metadata;
|
|
2521
|
+
this._updateProgress(this._blob.size());
|
|
2522
|
+
this._transition("success" /* InternalTaskState.SUCCESS */);
|
|
2523
|
+
}, this._errorHandler);
|
|
2524
|
+
});
|
|
2525
|
+
}
|
|
2526
|
+
_updateProgress(transferred) {
|
|
2527
|
+
const old = this._transferred;
|
|
2528
|
+
this._transferred = transferred;
|
|
2529
|
+
// A progress update can make the "transferred" value smaller (e.g. a
|
|
2530
|
+
// partial upload not completed by server, after which the "transferred"
|
|
2531
|
+
// value may reset to the value at the beginning of the request).
|
|
2532
|
+
if (this._transferred !== old) {
|
|
2533
|
+
this._notifyObservers();
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
_transition(state) {
|
|
2537
|
+
if (this._state === state) {
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
switch (state) {
|
|
2541
|
+
case "canceling" /* InternalTaskState.CANCELING */:
|
|
2542
|
+
case "pausing" /* InternalTaskState.PAUSING */:
|
|
2543
|
+
// TODO(andysoto):
|
|
2544
|
+
// assert(this.state_ === InternalTaskState.RUNNING ||
|
|
2545
|
+
// this.state_ === InternalTaskState.PAUSING);
|
|
2546
|
+
this._state = state;
|
|
2547
|
+
if (this._request !== undefined) {
|
|
2548
|
+
this._request.cancel();
|
|
2549
|
+
}
|
|
2550
|
+
else if (this.pendingTimeout) {
|
|
2551
|
+
clearTimeout(this.pendingTimeout);
|
|
2552
|
+
this.pendingTimeout = undefined;
|
|
2553
|
+
this.completeTransitions_();
|
|
2554
|
+
}
|
|
2555
|
+
break;
|
|
2556
|
+
case "running" /* InternalTaskState.RUNNING */:
|
|
2557
|
+
// TODO(andysoto):
|
|
2558
|
+
// assert(this.state_ === InternalTaskState.PAUSED ||
|
|
2559
|
+
// this.state_ === InternalTaskState.PAUSING);
|
|
2560
|
+
const wasPaused = this._state === "paused" /* InternalTaskState.PAUSED */;
|
|
2561
|
+
this._state = state;
|
|
2562
|
+
if (wasPaused) {
|
|
2563
|
+
this._notifyObservers();
|
|
2564
|
+
this._start();
|
|
2565
|
+
}
|
|
2566
|
+
break;
|
|
2567
|
+
case "paused" /* InternalTaskState.PAUSED */:
|
|
2568
|
+
// TODO(andysoto):
|
|
2569
|
+
// assert(this.state_ === InternalTaskState.PAUSING);
|
|
2570
|
+
this._state = state;
|
|
2571
|
+
this._notifyObservers();
|
|
2572
|
+
break;
|
|
2573
|
+
case "canceled" /* InternalTaskState.CANCELED */:
|
|
2574
|
+
// TODO(andysoto):
|
|
2575
|
+
// assert(this.state_ === InternalTaskState.PAUSED ||
|
|
2576
|
+
// this.state_ === InternalTaskState.CANCELING);
|
|
2577
|
+
this._error = canceled();
|
|
2578
|
+
this._state = state;
|
|
2579
|
+
this._notifyObservers();
|
|
2580
|
+
break;
|
|
2581
|
+
case "error" /* InternalTaskState.ERROR */:
|
|
2582
|
+
// TODO(andysoto):
|
|
2583
|
+
// assert(this.state_ === InternalTaskState.RUNNING ||
|
|
2584
|
+
// this.state_ === InternalTaskState.PAUSING ||
|
|
2585
|
+
// this.state_ === InternalTaskState.CANCELING);
|
|
2586
|
+
this._state = state;
|
|
2587
|
+
this._notifyObservers();
|
|
2588
|
+
break;
|
|
2589
|
+
case "success" /* InternalTaskState.SUCCESS */:
|
|
2590
|
+
// TODO(andysoto):
|
|
2591
|
+
// assert(this.state_ === InternalTaskState.RUNNING ||
|
|
2592
|
+
// this.state_ === InternalTaskState.PAUSING ||
|
|
2593
|
+
// this.state_ === InternalTaskState.CANCELING);
|
|
2594
|
+
this._state = state;
|
|
2595
|
+
this._notifyObservers();
|
|
2596
|
+
break;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
completeTransitions_() {
|
|
2600
|
+
switch (this._state) {
|
|
2601
|
+
case "pausing" /* InternalTaskState.PAUSING */:
|
|
2602
|
+
this._transition("paused" /* InternalTaskState.PAUSED */);
|
|
2603
|
+
break;
|
|
2604
|
+
case "canceling" /* InternalTaskState.CANCELING */:
|
|
2605
|
+
this._transition("canceled" /* InternalTaskState.CANCELED */);
|
|
2606
|
+
break;
|
|
2607
|
+
case "running" /* InternalTaskState.RUNNING */:
|
|
2608
|
+
this._start();
|
|
2609
|
+
break;
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
/**
|
|
2613
|
+
* A snapshot of the current task state.
|
|
2614
|
+
*/
|
|
2615
|
+
get snapshot() {
|
|
2616
|
+
const externalState = taskStateFromInternalTaskState(this._state);
|
|
2617
|
+
return {
|
|
2618
|
+
bytesTransferred: this._transferred,
|
|
2619
|
+
totalBytes: this._blob.size(),
|
|
2620
|
+
state: externalState,
|
|
2621
|
+
metadata: this._metadata,
|
|
2622
|
+
task: this,
|
|
2623
|
+
ref: this._ref
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2626
|
+
/**
|
|
2627
|
+
* Adds a callback for an event.
|
|
2628
|
+
* @param type - The type of event to listen for.
|
|
2629
|
+
* @param nextOrObserver -
|
|
2630
|
+
* The `next` function, which gets called for each item in
|
|
2631
|
+
* the event stream, or an observer object with some or all of these three
|
|
2632
|
+
* properties (`next`, `error`, `complete`).
|
|
2633
|
+
* @param error - A function that gets called with a `StorageError`
|
|
2634
|
+
* if the event stream ends due to an error.
|
|
2635
|
+
* @param completed - A function that gets called if the
|
|
2636
|
+
* event stream ends normally.
|
|
2637
|
+
* @returns
|
|
2638
|
+
* If only the event argument is passed, returns a function you can use to
|
|
2639
|
+
* add callbacks (see the examples above). If more than just the event
|
|
2640
|
+
* argument is passed, returns a function you can call to unregister the
|
|
2641
|
+
* callbacks.
|
|
2642
|
+
*/
|
|
2643
|
+
on(type, nextOrObserver, error, completed) {
|
|
2644
|
+
// Note: `type` isn't being used. Its type is also incorrect. TaskEvent should not be a string.
|
|
2645
|
+
const observer = new Observer(nextOrObserver || undefined, error || undefined, completed || undefined);
|
|
2646
|
+
this._addObserver(observer);
|
|
2647
|
+
return () => {
|
|
2648
|
+
this._removeObserver(observer);
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
/**
|
|
2652
|
+
* This object behaves like a Promise, and resolves with its snapshot data
|
|
2653
|
+
* when the upload completes.
|
|
2654
|
+
* @param onFulfilled - The fulfillment callback. Promise chaining works as normal.
|
|
2655
|
+
* @param onRejected - The rejection callback.
|
|
2656
|
+
*/
|
|
2657
|
+
then(onFulfilled, onRejected) {
|
|
2658
|
+
// These casts are needed so that TypeScript can infer the types of the
|
|
2659
|
+
// resulting Promise.
|
|
2660
|
+
return this._promise.then(onFulfilled, onRejected);
|
|
2661
|
+
}
|
|
2662
|
+
/**
|
|
2663
|
+
* Equivalent to calling `then(null, onRejected)`.
|
|
2664
|
+
*/
|
|
2665
|
+
catch(onRejected) {
|
|
2666
|
+
return this.then(null, onRejected);
|
|
2667
|
+
}
|
|
2668
|
+
/**
|
|
2669
|
+
* Adds the given observer.
|
|
2670
|
+
*/
|
|
2671
|
+
_addObserver(observer) {
|
|
2672
|
+
this._observers.push(observer);
|
|
2673
|
+
this._notifyObserver(observer);
|
|
2674
|
+
}
|
|
2675
|
+
/**
|
|
2676
|
+
* Removes the given observer.
|
|
2677
|
+
*/
|
|
2678
|
+
_removeObserver(observer) {
|
|
2679
|
+
const i = this._observers.indexOf(observer);
|
|
2680
|
+
if (i !== -1) {
|
|
2681
|
+
this._observers.splice(i, 1);
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
_notifyObservers() {
|
|
2685
|
+
this._finishPromise();
|
|
2686
|
+
const observers = this._observers.slice();
|
|
2687
|
+
observers.forEach(observer => {
|
|
2688
|
+
this._notifyObserver(observer);
|
|
2689
|
+
});
|
|
2690
|
+
}
|
|
2691
|
+
_finishPromise() {
|
|
2692
|
+
if (this._resolve !== undefined) {
|
|
2693
|
+
let triggered = true;
|
|
2694
|
+
switch (taskStateFromInternalTaskState(this._state)) {
|
|
2695
|
+
case TaskState.SUCCESS:
|
|
2696
|
+
async(this._resolve.bind(null, this.snapshot))();
|
|
2697
|
+
break;
|
|
2698
|
+
case TaskState.CANCELED:
|
|
2699
|
+
case TaskState.ERROR:
|
|
2700
|
+
const toCall = this._reject;
|
|
2701
|
+
async(toCall.bind(null, this._error))();
|
|
2702
|
+
break;
|
|
2703
|
+
default:
|
|
2704
|
+
triggered = false;
|
|
2705
|
+
break;
|
|
2706
|
+
}
|
|
2707
|
+
if (triggered) {
|
|
2708
|
+
this._resolve = undefined;
|
|
2709
|
+
this._reject = undefined;
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
_notifyObserver(observer) {
|
|
2714
|
+
const externalState = taskStateFromInternalTaskState(this._state);
|
|
2715
|
+
switch (externalState) {
|
|
2716
|
+
case TaskState.RUNNING:
|
|
2717
|
+
case TaskState.PAUSED:
|
|
2718
|
+
if (observer.next) {
|
|
2719
|
+
async(observer.next.bind(observer, this.snapshot))();
|
|
2720
|
+
}
|
|
2721
|
+
break;
|
|
2722
|
+
case TaskState.SUCCESS:
|
|
2723
|
+
if (observer.complete) {
|
|
2724
|
+
async(observer.complete.bind(observer))();
|
|
2725
|
+
}
|
|
2726
|
+
break;
|
|
2727
|
+
case TaskState.CANCELED:
|
|
2728
|
+
case TaskState.ERROR:
|
|
2729
|
+
if (observer.error) {
|
|
2730
|
+
async(observer.error.bind(observer, this._error))();
|
|
2731
|
+
}
|
|
2732
|
+
break;
|
|
2733
|
+
default:
|
|
2734
|
+
// TODO(andysoto): assert(false);
|
|
2735
|
+
if (observer.error) {
|
|
2736
|
+
async(observer.error.bind(observer, this._error))();
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
/**
|
|
2741
|
+
* Resumes a paused task. Has no effect on a currently running or failed task.
|
|
2742
|
+
* @returns True if the operation took effect, false if ignored.
|
|
2743
|
+
*/
|
|
2744
|
+
resume() {
|
|
2745
|
+
const valid = this._state === "paused" /* InternalTaskState.PAUSED */ ||
|
|
2746
|
+
this._state === "pausing" /* InternalTaskState.PAUSING */;
|
|
2747
|
+
if (valid) {
|
|
2748
|
+
this._transition("running" /* InternalTaskState.RUNNING */);
|
|
2749
|
+
}
|
|
2750
|
+
return valid;
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2753
|
+
* Pauses a currently running task. Has no effect on a paused or failed task.
|
|
2754
|
+
* @returns True if the operation took effect, false if ignored.
|
|
2755
|
+
*/
|
|
2756
|
+
pause() {
|
|
2757
|
+
const valid = this._state === "running" /* InternalTaskState.RUNNING */;
|
|
2758
|
+
if (valid) {
|
|
2759
|
+
this._transition("pausing" /* InternalTaskState.PAUSING */);
|
|
2760
|
+
}
|
|
2761
|
+
return valid;
|
|
2762
|
+
}
|
|
2763
|
+
/**
|
|
2764
|
+
* Cancels a currently running or paused task. Has no effect on a complete or
|
|
2765
|
+
* failed task.
|
|
2766
|
+
* @returns True if the operation took effect, false if ignored.
|
|
2767
|
+
*/
|
|
2768
|
+
cancel() {
|
|
2769
|
+
const valid = this._state === "running" /* InternalTaskState.RUNNING */ ||
|
|
2770
|
+
this._state === "pausing" /* InternalTaskState.PAUSING */;
|
|
2771
|
+
if (valid) {
|
|
2772
|
+
this._transition("canceling" /* InternalTaskState.CANCELING */);
|
|
2773
|
+
}
|
|
2774
|
+
return valid;
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
/**
|
|
2779
|
+
* @license
|
|
2780
|
+
* Copyright 2019 Google LLC
|
|
2781
|
+
*
|
|
2782
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2783
|
+
* you may not use this file except in compliance with the License.
|
|
2784
|
+
* You may obtain a copy of the License at
|
|
2785
|
+
*
|
|
2786
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2787
|
+
*
|
|
2788
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2789
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2790
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2791
|
+
* See the License for the specific language governing permissions and
|
|
2792
|
+
* limitations under the License.
|
|
2793
|
+
*/
|
|
2794
|
+
/**
|
|
2795
|
+
* Provides methods to interact with a bucket in the Firebase Storage service.
|
|
2796
|
+
* @internal
|
|
2797
|
+
* @param _location - An fbs.location, or the URL at
|
|
2798
|
+
* which to base this object, in one of the following forms:
|
|
2799
|
+
* gs://<bucket>/<object-path>
|
|
2800
|
+
* http[s]://firebasestorage.googleapis.com/
|
|
2801
|
+
* <api-version>/b/<bucket>/o/<object-path>
|
|
2802
|
+
* Any query or fragment strings will be ignored in the http[s]
|
|
2803
|
+
* format. If no value is passed, the storage object will use a URL based on
|
|
2804
|
+
* the project ID of the base firebase.App instance.
|
|
2805
|
+
*/
|
|
2806
|
+
class Reference {
|
|
2807
|
+
constructor(_service, location) {
|
|
2808
|
+
this._service = _service;
|
|
2809
|
+
if (location instanceof Location) {
|
|
2810
|
+
this._location = location;
|
|
2811
|
+
}
|
|
2812
|
+
else {
|
|
2813
|
+
this._location = Location.makeFromUrl(location, _service.host);
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
/**
|
|
2817
|
+
* Returns the URL for the bucket and path this object references,
|
|
2818
|
+
* in the form gs://<bucket>/<object-path>
|
|
2819
|
+
* @override
|
|
2820
|
+
*/
|
|
2821
|
+
toString() {
|
|
2822
|
+
return 'gs://' + this._location.bucket + '/' + this._location.path;
|
|
2823
|
+
}
|
|
2824
|
+
_newRef(service, location) {
|
|
2825
|
+
return new Reference(service, location);
|
|
2826
|
+
}
|
|
2827
|
+
/**
|
|
2828
|
+
* A reference to the root of this object's bucket.
|
|
2829
|
+
*/
|
|
2830
|
+
get root() {
|
|
2831
|
+
const location = new Location(this._location.bucket, '');
|
|
2832
|
+
return this._newRef(this._service, location);
|
|
2833
|
+
}
|
|
2834
|
+
/**
|
|
2835
|
+
* The name of the bucket containing this reference's object.
|
|
2836
|
+
*/
|
|
2837
|
+
get bucket() {
|
|
2838
|
+
return this._location.bucket;
|
|
2839
|
+
}
|
|
2840
|
+
/**
|
|
2841
|
+
* The full path of this object.
|
|
2842
|
+
*/
|
|
2843
|
+
get fullPath() {
|
|
2844
|
+
return this._location.path;
|
|
2845
|
+
}
|
|
2846
|
+
/**
|
|
2847
|
+
* The short name of this object, which is the last component of the full path.
|
|
2848
|
+
* For example, if fullPath is 'full/path/image.png', name is 'image.png'.
|
|
2849
|
+
*/
|
|
2850
|
+
get name() {
|
|
2851
|
+
return lastComponent(this._location.path);
|
|
2852
|
+
}
|
|
2853
|
+
/**
|
|
2854
|
+
* The `StorageService` instance this `StorageReference` is associated with.
|
|
2855
|
+
*/
|
|
2856
|
+
get storage() {
|
|
2857
|
+
return this._service;
|
|
2858
|
+
}
|
|
2859
|
+
/**
|
|
2860
|
+
* A `StorageReference` pointing to the parent location of this `StorageReference`, or null if
|
|
2861
|
+
* this reference is the root.
|
|
2862
|
+
*/
|
|
2863
|
+
get parent() {
|
|
2864
|
+
const newPath = parent(this._location.path);
|
|
2865
|
+
if (newPath === null) {
|
|
2866
|
+
return null;
|
|
2867
|
+
}
|
|
2868
|
+
const location = new Location(this._location.bucket, newPath);
|
|
2869
|
+
return new Reference(this._service, location);
|
|
2870
|
+
}
|
|
2871
|
+
/**
|
|
2872
|
+
* Utility function to throw an error in methods that do not accept a root reference.
|
|
2873
|
+
*/
|
|
2874
|
+
_throwIfRoot(name) {
|
|
2875
|
+
if (this._location.path === '') {
|
|
2876
|
+
throw invalidRootOperation(name);
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
/**
|
|
2881
|
+
* Download the bytes at the object's location.
|
|
2882
|
+
* @returns A Promise containing the downloaded bytes.
|
|
2883
|
+
*/
|
|
2884
|
+
function getBytesInternal(ref, maxDownloadSizeBytes) {
|
|
2885
|
+
ref._throwIfRoot('getBytes');
|
|
2886
|
+
const requestInfo = getBytes$1(ref.storage, ref._location, maxDownloadSizeBytes);
|
|
2887
|
+
return ref.storage
|
|
2888
|
+
.makeRequestWithTokens(requestInfo, newBytesConnection)
|
|
2889
|
+
.then(bytes => maxDownloadSizeBytes !== undefined
|
|
2890
|
+
? // GCS may not honor the Range header for small files
|
|
2891
|
+
bytes.slice(0, maxDownloadSizeBytes)
|
|
2892
|
+
: bytes);
|
|
2893
|
+
}
|
|
2894
|
+
/** Stream the bytes at the object's location. */
|
|
2895
|
+
function getStreamInternal(ref, maxDownloadSizeBytes) {
|
|
2896
|
+
ref._throwIfRoot('getStream');
|
|
2897
|
+
const requestInfo = getBytes$1(ref.storage, ref._location, maxDownloadSizeBytes);
|
|
2898
|
+
// Transforms the stream so that only `maxDownloadSizeBytes` bytes are piped to the result
|
|
2899
|
+
const newMaxSizeTransform = (n) => {
|
|
2900
|
+
let missingBytes = n;
|
|
2901
|
+
return {
|
|
2902
|
+
transform(chunk, controller) {
|
|
2903
|
+
// GCS may not honor the Range header for small files
|
|
2904
|
+
if (chunk.length < missingBytes) {
|
|
2905
|
+
controller.enqueue(chunk);
|
|
2906
|
+
missingBytes -= chunk.length;
|
|
2907
|
+
}
|
|
2908
|
+
else {
|
|
2909
|
+
controller.enqueue(chunk.slice(0, missingBytes));
|
|
2910
|
+
controller.terminate();
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
};
|
|
2914
|
+
};
|
|
2915
|
+
const result = maxDownloadSizeBytes !== undefined
|
|
2916
|
+
? new TransformStream(newMaxSizeTransform(maxDownloadSizeBytes))
|
|
2917
|
+
: new TransformStream(); // The default transformer forwards all chunks to its readable side
|
|
2918
|
+
ref.storage
|
|
2919
|
+
.makeRequestWithTokens(requestInfo, newStreamConnection)
|
|
2920
|
+
.then(readableStream => readableStream.pipeThrough(result))
|
|
2921
|
+
.catch(err => result.writable.abort(err));
|
|
2922
|
+
return result.readable;
|
|
2923
|
+
}
|
|
2924
|
+
/**
|
|
2925
|
+
* Uploads data to this object's location.
|
|
2926
|
+
* The upload is not resumable.
|
|
2927
|
+
*
|
|
2928
|
+
* @param ref - StorageReference where data should be uploaded.
|
|
2929
|
+
* @param data - The data to upload.
|
|
2930
|
+
* @param metadata - Metadata for the newly uploaded data.
|
|
2931
|
+
* @returns A Promise containing an UploadResult
|
|
2932
|
+
*/
|
|
2933
|
+
function uploadBytes$1(ref, data, metadata) {
|
|
2934
|
+
ref._throwIfRoot('uploadBytes');
|
|
2935
|
+
const requestInfo = multipartUpload(ref.storage, ref._location, getMappings(), new FbsBlob(data, true), metadata);
|
|
2936
|
+
return ref.storage
|
|
2937
|
+
.makeRequestWithTokens(requestInfo, newTextConnection)
|
|
2938
|
+
.then(finalMetadata => {
|
|
2939
|
+
return {
|
|
2940
|
+
metadata: finalMetadata,
|
|
2941
|
+
ref
|
|
2942
|
+
};
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
/**
|
|
2946
|
+
* Uploads data to this object's location.
|
|
2947
|
+
* The upload can be paused and resumed, and exposes progress updates.
|
|
2948
|
+
* @public
|
|
2949
|
+
* @param ref - StorageReference where data should be uploaded.
|
|
2950
|
+
* @param data - The data to upload.
|
|
2951
|
+
* @param metadata - Metadata for the newly uploaded data.
|
|
2952
|
+
* @returns An UploadTask
|
|
2953
|
+
*/
|
|
2954
|
+
function uploadBytesResumable$1(ref, data, metadata) {
|
|
2955
|
+
ref._throwIfRoot('uploadBytesResumable');
|
|
2956
|
+
return new UploadTask(ref, new FbsBlob(data), metadata);
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Uploads a string to this object's location.
|
|
2960
|
+
* The upload is not resumable.
|
|
2961
|
+
* @public
|
|
2962
|
+
* @param ref - StorageReference where string should be uploaded.
|
|
2963
|
+
* @param value - The string to upload.
|
|
2964
|
+
* @param format - The format of the string to upload.
|
|
2965
|
+
* @param metadata - Metadata for the newly uploaded string.
|
|
2966
|
+
* @returns A Promise containing an UploadResult
|
|
2967
|
+
*/
|
|
2968
|
+
function uploadString$1(ref, value, format = StringFormat.RAW, metadata) {
|
|
2969
|
+
ref._throwIfRoot('uploadString');
|
|
2970
|
+
const data = dataFromString(format, value);
|
|
2971
|
+
const metadataClone = { ...metadata };
|
|
2972
|
+
if (metadataClone['contentType'] == null && data.contentType != null) {
|
|
2973
|
+
metadataClone['contentType'] = data.contentType;
|
|
2974
|
+
}
|
|
2975
|
+
return uploadBytes$1(ref, data.data, metadataClone);
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2978
|
+
* List all items (files) and prefixes (folders) under this storage reference.
|
|
2979
|
+
*
|
|
2980
|
+
* This is a helper method for calling list() repeatedly until there are
|
|
2981
|
+
* no more results. The default pagination size is 1000.
|
|
2982
|
+
*
|
|
2983
|
+
* Note: The results may not be consistent if objects are changed while this
|
|
2984
|
+
* operation is running.
|
|
2985
|
+
*
|
|
2986
|
+
* Warning: listAll may potentially consume too many resources if there are
|
|
2987
|
+
* too many results.
|
|
2988
|
+
* @public
|
|
2989
|
+
* @param ref - StorageReference to get list from.
|
|
2990
|
+
*
|
|
2991
|
+
* @returns A Promise that resolves with all the items and prefixes under
|
|
2992
|
+
* the current storage reference. `prefixes` contains references to
|
|
2993
|
+
* sub-directories and `items` contains references to objects in this
|
|
2994
|
+
* folder. `nextPageToken` is never returned.
|
|
2995
|
+
*/
|
|
2996
|
+
function listAll$1(ref) {
|
|
2997
|
+
const accumulator = {
|
|
2998
|
+
prefixes: [],
|
|
2999
|
+
items: []
|
|
3000
|
+
};
|
|
3001
|
+
return listAllHelper(ref, accumulator).then(() => accumulator);
|
|
3002
|
+
}
|
|
3003
|
+
/**
|
|
3004
|
+
* Separated from listAll because async functions can't use "arguments".
|
|
3005
|
+
* @param ref
|
|
3006
|
+
* @param accumulator
|
|
3007
|
+
* @param pageToken
|
|
3008
|
+
*/
|
|
3009
|
+
async function listAllHelper(ref, accumulator, pageToken) {
|
|
3010
|
+
const opt = {
|
|
3011
|
+
// maxResults is 1000 by default.
|
|
3012
|
+
pageToken
|
|
3013
|
+
};
|
|
3014
|
+
const nextPage = await list$1(ref, opt);
|
|
3015
|
+
accumulator.prefixes.push(...nextPage.prefixes);
|
|
3016
|
+
accumulator.items.push(...nextPage.items);
|
|
3017
|
+
if (nextPage.nextPageToken != null) {
|
|
3018
|
+
await listAllHelper(ref, accumulator, nextPage.nextPageToken);
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
/**
|
|
3022
|
+
* List items (files) and prefixes (folders) under this storage reference.
|
|
3023
|
+
*
|
|
3024
|
+
* List API is only available for Firebase Rules Version 2.
|
|
3025
|
+
*
|
|
3026
|
+
* GCS is a key-blob store. Firebase Storage imposes the semantic of '/'
|
|
3027
|
+
* delimited folder structure.
|
|
3028
|
+
* Refer to GCS's List API if you want to learn more.
|
|
3029
|
+
*
|
|
3030
|
+
* To adhere to Firebase Rules's Semantics, Firebase Storage does not
|
|
3031
|
+
* support objects whose paths end with "/" or contain two consecutive
|
|
3032
|
+
* "/"s. Firebase Storage List API will filter these unsupported objects.
|
|
3033
|
+
* list() may fail if there are too many unsupported objects in the bucket.
|
|
3034
|
+
* @public
|
|
3035
|
+
*
|
|
3036
|
+
* @param ref - StorageReference to get list from.
|
|
3037
|
+
* @param options - See ListOptions for details.
|
|
3038
|
+
* @returns A Promise that resolves with the items and prefixes.
|
|
3039
|
+
* `prefixes` contains references to sub-folders and `items`
|
|
3040
|
+
* contains references to objects in this folder. `nextPageToken`
|
|
3041
|
+
* can be used to get the rest of the results.
|
|
3042
|
+
*/
|
|
3043
|
+
function list$1(ref, options) {
|
|
3044
|
+
if (options != null) {
|
|
3045
|
+
if (typeof options.maxResults === 'number') {
|
|
3046
|
+
validateNumber('options.maxResults',
|
|
3047
|
+
/* minValue= */ 1,
|
|
3048
|
+
/* maxValue= */ 1000, options.maxResults);
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
const op = options || {};
|
|
3052
|
+
const requestInfo = list$2(ref.storage, ref._location,
|
|
3053
|
+
/*delimiter= */ '/', op.pageToken, op.maxResults);
|
|
3054
|
+
return ref.storage.makeRequestWithTokens(requestInfo, newTextConnection);
|
|
3055
|
+
}
|
|
3056
|
+
/**
|
|
3057
|
+
* A `Promise` that resolves with the metadata for this object. If this
|
|
3058
|
+
* object doesn't exist or metadata cannot be retrieved, the promise is
|
|
3059
|
+
* rejected.
|
|
3060
|
+
* @public
|
|
3061
|
+
* @param ref - StorageReference to get metadata from.
|
|
3062
|
+
*/
|
|
3063
|
+
function getMetadata$1(ref) {
|
|
3064
|
+
ref._throwIfRoot('getMetadata');
|
|
3065
|
+
const requestInfo = getMetadata$2(ref.storage, ref._location, getMappings());
|
|
3066
|
+
return ref.storage.makeRequestWithTokens(requestInfo, newTextConnection);
|
|
3067
|
+
}
|
|
3068
|
+
/**
|
|
3069
|
+
* Updates the metadata for this object.
|
|
3070
|
+
* @public
|
|
3071
|
+
* @param ref - StorageReference to update metadata for.
|
|
3072
|
+
* @param metadata - The new metadata for the object.
|
|
3073
|
+
* Only values that have been explicitly set will be changed. Explicitly
|
|
3074
|
+
* setting a value to null will remove the metadata.
|
|
3075
|
+
* @returns A `Promise` that resolves
|
|
3076
|
+
* with the new metadata for this object.
|
|
3077
|
+
* See `firebaseStorage.Reference.prototype.getMetadata`
|
|
3078
|
+
*/
|
|
3079
|
+
function updateMetadata$1(ref, metadata) {
|
|
3080
|
+
ref._throwIfRoot('updateMetadata');
|
|
3081
|
+
const requestInfo = updateMetadata$2(ref.storage, ref._location, metadata, getMappings());
|
|
3082
|
+
return ref.storage.makeRequestWithTokens(requestInfo, newTextConnection);
|
|
3083
|
+
}
|
|
3084
|
+
/**
|
|
3085
|
+
* Returns the download URL for the given Reference.
|
|
3086
|
+
* @public
|
|
3087
|
+
* @returns A `Promise` that resolves with the download
|
|
3088
|
+
* URL for this object.
|
|
3089
|
+
*/
|
|
3090
|
+
function getDownloadURL$1(ref) {
|
|
3091
|
+
ref._throwIfRoot('getDownloadURL');
|
|
3092
|
+
const requestInfo = getDownloadUrl(ref.storage, ref._location, getMappings());
|
|
3093
|
+
return ref.storage
|
|
3094
|
+
.makeRequestWithTokens(requestInfo, newTextConnection)
|
|
3095
|
+
.then(url => {
|
|
3096
|
+
if (url === null) {
|
|
3097
|
+
throw noDownloadURL();
|
|
3098
|
+
}
|
|
3099
|
+
return url;
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
/**
|
|
3103
|
+
* Deletes the object at this location.
|
|
3104
|
+
* @public
|
|
3105
|
+
* @param ref - StorageReference for object to delete.
|
|
3106
|
+
* @returns A `Promise` that resolves if the deletion succeeds.
|
|
3107
|
+
*/
|
|
3108
|
+
function deleteObject$1(ref) {
|
|
3109
|
+
ref._throwIfRoot('deleteObject');
|
|
3110
|
+
const requestInfo = deleteObject$2(ref.storage, ref._location);
|
|
3111
|
+
return ref.storage.makeRequestWithTokens(requestInfo, newTextConnection);
|
|
3112
|
+
}
|
|
3113
|
+
/**
|
|
3114
|
+
* Returns reference for object obtained by appending `childPath` to `ref`.
|
|
3115
|
+
*
|
|
3116
|
+
* @param ref - StorageReference to get child of.
|
|
3117
|
+
* @param childPath - Child path from provided ref.
|
|
3118
|
+
* @returns A reference to the object obtained by
|
|
3119
|
+
* appending childPath, removing any duplicate, beginning, or trailing
|
|
3120
|
+
* slashes.
|
|
3121
|
+
*
|
|
3122
|
+
*/
|
|
3123
|
+
function _getChild$1(ref, childPath) {
|
|
3124
|
+
const newPath = child(ref._location.path, childPath);
|
|
3125
|
+
const location = new Location(ref._location.bucket, newPath);
|
|
3126
|
+
return new Reference(ref.storage, location);
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
/**
|
|
3130
|
+
* @license
|
|
3131
|
+
* Copyright 2017 Google LLC
|
|
3132
|
+
*
|
|
3133
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3134
|
+
* you may not use this file except in compliance with the License.
|
|
3135
|
+
* You may obtain a copy of the License at
|
|
3136
|
+
*
|
|
3137
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3138
|
+
*
|
|
3139
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3140
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3141
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3142
|
+
* See the License for the specific language governing permissions and
|
|
3143
|
+
* limitations under the License.
|
|
3144
|
+
*/
|
|
3145
|
+
function isUrl(path) {
|
|
3146
|
+
return /^[A-Za-z]+:\/\//.test(path);
|
|
3147
|
+
}
|
|
3148
|
+
/**
|
|
3149
|
+
* Returns a firebaseStorage.Reference for the given url.
|
|
3150
|
+
*/
|
|
3151
|
+
function refFromURL(service, url) {
|
|
3152
|
+
return new Reference(service, url);
|
|
3153
|
+
}
|
|
3154
|
+
/**
|
|
3155
|
+
* Returns a firebaseStorage.Reference for the given path in the default
|
|
3156
|
+
* bucket.
|
|
3157
|
+
*/
|
|
3158
|
+
function refFromPath(ref, path) {
|
|
3159
|
+
if (ref instanceof FirebaseStorageImpl) {
|
|
3160
|
+
const service = ref;
|
|
3161
|
+
if (service._bucket == null) {
|
|
3162
|
+
throw noDefaultBucket();
|
|
3163
|
+
}
|
|
3164
|
+
const reference = new Reference(service, service._bucket);
|
|
3165
|
+
if (path != null) {
|
|
3166
|
+
return refFromPath(reference, path);
|
|
3167
|
+
}
|
|
3168
|
+
else {
|
|
3169
|
+
return reference;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
else {
|
|
3173
|
+
// ref is a Reference
|
|
3174
|
+
if (path !== undefined) {
|
|
3175
|
+
return _getChild$1(ref, path);
|
|
3176
|
+
}
|
|
3177
|
+
else {
|
|
3178
|
+
return ref;
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
function ref$1(serviceOrRef, pathOrUrl) {
|
|
3183
|
+
if (pathOrUrl && isUrl(pathOrUrl)) {
|
|
3184
|
+
if (serviceOrRef instanceof FirebaseStorageImpl) {
|
|
3185
|
+
return refFromURL(serviceOrRef, pathOrUrl);
|
|
3186
|
+
}
|
|
3187
|
+
else {
|
|
3188
|
+
throw invalidArgument('To use ref(service, url), the first argument must be a Storage instance.');
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
else {
|
|
3192
|
+
return refFromPath(serviceOrRef, pathOrUrl);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
function extractBucket(host, config) {
|
|
3196
|
+
const bucketString = config?.[CONFIG_STORAGE_BUCKET_KEY];
|
|
3197
|
+
if (bucketString == null) {
|
|
3198
|
+
return null;
|
|
3199
|
+
}
|
|
3200
|
+
return Location.makeFromBucketSpec(bucketString, host);
|
|
3201
|
+
}
|
|
3202
|
+
function connectStorageEmulator$1(storage, host, port, options = {}) {
|
|
3203
|
+
storage.host = `${host}:${port}`;
|
|
3204
|
+
const useSsl = util.isCloudWorkstation(host);
|
|
3205
|
+
// Workaround to get cookies in Firebase Studio
|
|
3206
|
+
if (useSsl) {
|
|
3207
|
+
void util.pingServer(`https://${storage.host}/b`);
|
|
3208
|
+
util.updateEmulatorBanner('Storage', true);
|
|
3209
|
+
}
|
|
3210
|
+
storage._isUsingEmulator = true;
|
|
3211
|
+
storage._protocol = useSsl ? 'https' : 'http';
|
|
3212
|
+
const { mockUserToken } = options;
|
|
3213
|
+
if (mockUserToken) {
|
|
3214
|
+
storage._overrideAuthToken =
|
|
3215
|
+
typeof mockUserToken === 'string'
|
|
3216
|
+
? mockUserToken
|
|
3217
|
+
: util.createMockUserToken(mockUserToken, storage.app.options.projectId);
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
/**
|
|
3221
|
+
* A service that provides Firebase Storage Reference instances.
|
|
3222
|
+
* @param opt_url - gs:// url to a custom Storage Bucket
|
|
3223
|
+
*
|
|
3224
|
+
* @internal
|
|
3225
|
+
*/
|
|
3226
|
+
class FirebaseStorageImpl {
|
|
3227
|
+
constructor(
|
|
3228
|
+
/**
|
|
3229
|
+
* FirebaseApp associated with this StorageService instance.
|
|
3230
|
+
*/
|
|
3231
|
+
app, _authProvider,
|
|
3232
|
+
/**
|
|
3233
|
+
* @internal
|
|
3234
|
+
*/
|
|
3235
|
+
_appCheckProvider,
|
|
3236
|
+
/**
|
|
3237
|
+
* @internal
|
|
3238
|
+
*/
|
|
3239
|
+
_url, _firebaseVersion, _isUsingEmulator = false) {
|
|
3240
|
+
this.app = app;
|
|
3241
|
+
this._authProvider = _authProvider;
|
|
3242
|
+
this._appCheckProvider = _appCheckProvider;
|
|
3243
|
+
this._url = _url;
|
|
3244
|
+
this._firebaseVersion = _firebaseVersion;
|
|
3245
|
+
this._isUsingEmulator = _isUsingEmulator;
|
|
3246
|
+
this._bucket = null;
|
|
3247
|
+
/**
|
|
3248
|
+
* This string can be in the formats:
|
|
3249
|
+
* - host
|
|
3250
|
+
* - host:port
|
|
3251
|
+
*/
|
|
3252
|
+
this._host = DEFAULT_HOST;
|
|
3253
|
+
this._protocol = 'https';
|
|
3254
|
+
this._appId = null;
|
|
3255
|
+
this._deleted = false;
|
|
3256
|
+
this._maxOperationRetryTime = DEFAULT_MAX_OPERATION_RETRY_TIME;
|
|
3257
|
+
this._maxUploadRetryTime = DEFAULT_MAX_UPLOAD_RETRY_TIME;
|
|
3258
|
+
this._requests = new Set();
|
|
3259
|
+
if (_url != null) {
|
|
3260
|
+
this._bucket = Location.makeFromBucketSpec(_url, this._host);
|
|
3261
|
+
}
|
|
3262
|
+
else {
|
|
3263
|
+
this._bucket = extractBucket(this._host, this.app.options);
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
/**
|
|
3267
|
+
* The host string for this service, in the form of `host` or
|
|
3268
|
+
* `host:port`.
|
|
3269
|
+
*/
|
|
3270
|
+
get host() {
|
|
3271
|
+
return this._host;
|
|
3272
|
+
}
|
|
3273
|
+
set host(host) {
|
|
3274
|
+
this._host = host;
|
|
3275
|
+
if (this._url != null) {
|
|
3276
|
+
this._bucket = Location.makeFromBucketSpec(this._url, host);
|
|
3277
|
+
}
|
|
3278
|
+
else {
|
|
3279
|
+
this._bucket = extractBucket(host, this.app.options);
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
/**
|
|
3283
|
+
* The maximum time to retry uploads in milliseconds.
|
|
3284
|
+
*/
|
|
3285
|
+
get maxUploadRetryTime() {
|
|
3286
|
+
return this._maxUploadRetryTime;
|
|
3287
|
+
}
|
|
3288
|
+
set maxUploadRetryTime(time) {
|
|
3289
|
+
validateNumber('time',
|
|
3290
|
+
/* minValue=*/ 0,
|
|
3291
|
+
/* maxValue= */ Number.POSITIVE_INFINITY, time);
|
|
3292
|
+
this._maxUploadRetryTime = time;
|
|
3293
|
+
}
|
|
3294
|
+
/**
|
|
3295
|
+
* The maximum time to retry operations other than uploads or downloads in
|
|
3296
|
+
* milliseconds.
|
|
3297
|
+
*/
|
|
3298
|
+
get maxOperationRetryTime() {
|
|
3299
|
+
return this._maxOperationRetryTime;
|
|
3300
|
+
}
|
|
3301
|
+
set maxOperationRetryTime(time) {
|
|
3302
|
+
validateNumber('time',
|
|
3303
|
+
/* minValue=*/ 0,
|
|
3304
|
+
/* maxValue= */ Number.POSITIVE_INFINITY, time);
|
|
3305
|
+
this._maxOperationRetryTime = time;
|
|
3306
|
+
}
|
|
3307
|
+
async _getAuthToken() {
|
|
3308
|
+
if (this._overrideAuthToken) {
|
|
3309
|
+
return this._overrideAuthToken;
|
|
3310
|
+
}
|
|
3311
|
+
const auth = this._authProvider.getImmediate({ optional: true });
|
|
3312
|
+
if (auth) {
|
|
3313
|
+
const tokenData = await auth.getToken();
|
|
3314
|
+
if (tokenData !== null) {
|
|
3315
|
+
return tokenData.accessToken;
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
return null;
|
|
3319
|
+
}
|
|
3320
|
+
async _getAppCheckToken() {
|
|
3321
|
+
if (app._isFirebaseServerApp(this.app) && this.app.settings.appCheckToken) {
|
|
3322
|
+
return this.app.settings.appCheckToken;
|
|
3323
|
+
}
|
|
3324
|
+
const appCheck = this._appCheckProvider.getImmediate({ optional: true });
|
|
3325
|
+
if (appCheck) {
|
|
3326
|
+
const result = await appCheck.getToken();
|
|
3327
|
+
// TODO: What do we want to do if there is an error getting the token?
|
|
3328
|
+
// Context: appCheck.getToken() will never throw even if an error happened. In the error case, a dummy token will be
|
|
3329
|
+
// returned along with an error field describing the error. In general, we shouldn't care about the error condition and just use
|
|
3330
|
+
// the token (actual or dummy) to send requests.
|
|
3331
|
+
return result.token;
|
|
3332
|
+
}
|
|
3333
|
+
return null;
|
|
3334
|
+
}
|
|
3335
|
+
/**
|
|
3336
|
+
* Stop running requests and prevent more from being created.
|
|
3337
|
+
*/
|
|
3338
|
+
_delete() {
|
|
3339
|
+
if (!this._deleted) {
|
|
3340
|
+
this._deleted = true;
|
|
3341
|
+
this._requests.forEach(request => request.cancel());
|
|
3342
|
+
this._requests.clear();
|
|
3343
|
+
}
|
|
3344
|
+
return Promise.resolve();
|
|
3345
|
+
}
|
|
3346
|
+
/**
|
|
3347
|
+
* Returns a new firebaseStorage.Reference object referencing this StorageService
|
|
3348
|
+
* at the given Location.
|
|
3349
|
+
*/
|
|
3350
|
+
_makeStorageReference(loc) {
|
|
3351
|
+
return new Reference(this, loc);
|
|
3352
|
+
}
|
|
3353
|
+
/**
|
|
3354
|
+
* @param requestInfo - HTTP RequestInfo object
|
|
3355
|
+
* @param authToken - Firebase auth token
|
|
3356
|
+
*/
|
|
3357
|
+
_makeRequest(requestInfo, requestFactory, authToken, appCheckToken, retry = true) {
|
|
3358
|
+
if (!this._deleted) {
|
|
3359
|
+
const request = makeRequest(requestInfo, this._appId, authToken, appCheckToken, requestFactory, this._firebaseVersion, retry, this._isUsingEmulator);
|
|
3360
|
+
this._requests.add(request);
|
|
3361
|
+
// Request removes itself from set when complete.
|
|
3362
|
+
request.getPromise().then(() => this._requests.delete(request), () => this._requests.delete(request));
|
|
3363
|
+
return request;
|
|
3364
|
+
}
|
|
3365
|
+
else {
|
|
3366
|
+
return new FailRequest(appDeleted());
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
async makeRequestWithTokens(requestInfo, requestFactory) {
|
|
3370
|
+
const [authToken, appCheckToken] = await Promise.all([
|
|
3371
|
+
this._getAuthToken(),
|
|
3372
|
+
this._getAppCheckToken()
|
|
3373
|
+
]);
|
|
3374
|
+
return this._makeRequest(requestInfo, requestFactory, authToken, appCheckToken).getPromise();
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
const name = "@firebase/storage";
|
|
3379
|
+
const version = "0.14.1";
|
|
3380
|
+
|
|
3381
|
+
/**
|
|
3382
|
+
* @license
|
|
3383
|
+
* Copyright 2020 Google LLC
|
|
3384
|
+
*
|
|
3385
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3386
|
+
* you may not use this file except in compliance with the License.
|
|
3387
|
+
* You may obtain a copy of the License at
|
|
3388
|
+
*
|
|
3389
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3390
|
+
*
|
|
3391
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3392
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3393
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3394
|
+
* See the License for the specific language governing permissions and
|
|
3395
|
+
* limitations under the License.
|
|
3396
|
+
*/
|
|
3397
|
+
/**
|
|
3398
|
+
* Type constant for Firebase Storage.
|
|
3399
|
+
*/
|
|
3400
|
+
const STORAGE_TYPE = 'storage';
|
|
3401
|
+
|
|
3402
|
+
/**
|
|
3403
|
+
* @license
|
|
3404
|
+
* Copyright 2020 Google LLC
|
|
3405
|
+
*
|
|
3406
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3407
|
+
* you may not use this file except in compliance with the License.
|
|
3408
|
+
* You may obtain a copy of the License at
|
|
3409
|
+
*
|
|
3410
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3411
|
+
*
|
|
3412
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3413
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3414
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3415
|
+
* See the License for the specific language governing permissions and
|
|
3416
|
+
* limitations under the License.
|
|
3417
|
+
*/
|
|
3418
|
+
/**
|
|
3419
|
+
* Downloads the data at the object's location. Returns an error if the object
|
|
3420
|
+
* is not found.
|
|
3421
|
+
*
|
|
3422
|
+
* To use this functionality, you have to whitelist your app's origin in your
|
|
3423
|
+
* Cloud Storage bucket. See also
|
|
3424
|
+
* https://cloud.google.com/storage/docs/configuring-cors
|
|
3425
|
+
*
|
|
3426
|
+
* @public
|
|
3427
|
+
* @param ref - StorageReference where data should be downloaded.
|
|
3428
|
+
* @param maxDownloadSizeBytes - If set, the maximum allowed size in bytes to
|
|
3429
|
+
* retrieve.
|
|
3430
|
+
* @returns A Promise containing the object's bytes
|
|
3431
|
+
*/
|
|
3432
|
+
function getBytes(ref, maxDownloadSizeBytes) {
|
|
3433
|
+
ref = util.getModularInstance(ref);
|
|
3434
|
+
return getBytesInternal(ref, maxDownloadSizeBytes);
|
|
3435
|
+
}
|
|
3436
|
+
/**
|
|
3437
|
+
* Uploads data to this object's location.
|
|
3438
|
+
* The upload is not resumable.
|
|
3439
|
+
* @public
|
|
3440
|
+
* @param ref - {@link StorageReference} where data should be uploaded.
|
|
3441
|
+
* @param data - The data to upload.
|
|
3442
|
+
* @param metadata - Metadata for the data to upload.
|
|
3443
|
+
* @returns A Promise containing an UploadResult
|
|
3444
|
+
*/
|
|
3445
|
+
function uploadBytes(ref, data, metadata) {
|
|
3446
|
+
ref = util.getModularInstance(ref);
|
|
3447
|
+
return uploadBytes$1(ref, data, metadata);
|
|
3448
|
+
}
|
|
3449
|
+
/**
|
|
3450
|
+
* Uploads a string to this object's location.
|
|
3451
|
+
* The upload is not resumable.
|
|
3452
|
+
* @public
|
|
3453
|
+
* @param ref - {@link StorageReference} where string should be uploaded.
|
|
3454
|
+
* @param value - The string to upload.
|
|
3455
|
+
* @param format - The format of the string to upload.
|
|
3456
|
+
* @param metadata - Metadata for the string to upload.
|
|
3457
|
+
* @returns A Promise containing an UploadResult
|
|
3458
|
+
*/
|
|
3459
|
+
function uploadString(ref, value, format, metadata) {
|
|
3460
|
+
ref = util.getModularInstance(ref);
|
|
3461
|
+
return uploadString$1(ref, value, format, metadata);
|
|
3462
|
+
}
|
|
3463
|
+
/**
|
|
3464
|
+
* Uploads data to this object's location.
|
|
3465
|
+
* The upload can be paused and resumed, and exposes progress updates.
|
|
3466
|
+
* @public
|
|
3467
|
+
* @param ref - {@link StorageReference} where data should be uploaded.
|
|
3468
|
+
* @param data - The data to upload.
|
|
3469
|
+
* @param metadata - Metadata for the data to upload.
|
|
3470
|
+
* @returns An UploadTask
|
|
3471
|
+
*/
|
|
3472
|
+
function uploadBytesResumable(ref, data, metadata) {
|
|
3473
|
+
ref = util.getModularInstance(ref);
|
|
3474
|
+
return uploadBytesResumable$1(ref, data, metadata);
|
|
3475
|
+
}
|
|
3476
|
+
/**
|
|
3477
|
+
* A `Promise` that resolves with the metadata for this object. If this
|
|
3478
|
+
* object doesn't exist or metadata cannot be retrieved, the promise is
|
|
3479
|
+
* rejected.
|
|
3480
|
+
* @public
|
|
3481
|
+
* @param ref - {@link StorageReference} to get metadata from.
|
|
3482
|
+
*/
|
|
3483
|
+
function getMetadata(ref) {
|
|
3484
|
+
ref = util.getModularInstance(ref);
|
|
3485
|
+
return getMetadata$1(ref);
|
|
3486
|
+
}
|
|
3487
|
+
/**
|
|
3488
|
+
* Updates the metadata for this object.
|
|
3489
|
+
* @public
|
|
3490
|
+
* @param ref - {@link StorageReference} to update metadata for.
|
|
3491
|
+
* @param metadata - The new metadata for the object.
|
|
3492
|
+
* Only values that have been explicitly set will be changed. Explicitly
|
|
3493
|
+
* setting a value to null will remove the metadata.
|
|
3494
|
+
* @returns A `Promise` that resolves with the new metadata for this object.
|
|
3495
|
+
*/
|
|
3496
|
+
function updateMetadata(ref, metadata) {
|
|
3497
|
+
ref = util.getModularInstance(ref);
|
|
3498
|
+
return updateMetadata$1(ref, metadata);
|
|
3499
|
+
}
|
|
3500
|
+
/**
|
|
3501
|
+
* List items (files) and prefixes (folders) under this storage reference.
|
|
3502
|
+
*
|
|
3503
|
+
* List API is only available for Firebase Rules Version 2.
|
|
3504
|
+
*
|
|
3505
|
+
* GCS is a key-blob store. Firebase Storage imposes the semantic of '/'
|
|
3506
|
+
* delimited folder structure.
|
|
3507
|
+
* Refer to GCS's List API if you want to learn more.
|
|
3508
|
+
*
|
|
3509
|
+
* To adhere to Firebase Rules's Semantics, Firebase Storage does not
|
|
3510
|
+
* support objects whose paths end with "/" or contain two consecutive
|
|
3511
|
+
* "/"s. Firebase Storage List API will filter these unsupported objects.
|
|
3512
|
+
* list() may fail if there are too many unsupported objects in the bucket.
|
|
3513
|
+
* @public
|
|
3514
|
+
*
|
|
3515
|
+
* @param ref - {@link StorageReference} to get list from.
|
|
3516
|
+
* @param options - See {@link ListOptions} for details.
|
|
3517
|
+
* @returns A `Promise` that resolves with the items and prefixes.
|
|
3518
|
+
* `prefixes` contains references to sub-folders and `items`
|
|
3519
|
+
* contains references to objects in this folder. `nextPageToken`
|
|
3520
|
+
* can be used to get the rest of the results.
|
|
3521
|
+
*/
|
|
3522
|
+
function list(ref, options) {
|
|
3523
|
+
ref = util.getModularInstance(ref);
|
|
3524
|
+
return list$1(ref, options);
|
|
3525
|
+
}
|
|
3526
|
+
/**
|
|
3527
|
+
* List all items (files) and prefixes (folders) under this storage reference.
|
|
3528
|
+
*
|
|
3529
|
+
* This is a helper method for calling list() repeatedly until there are
|
|
3530
|
+
* no more results. The default pagination size is 1000.
|
|
3531
|
+
*
|
|
3532
|
+
* Note: The results may not be consistent if objects are changed while this
|
|
3533
|
+
* operation is running.
|
|
3534
|
+
*
|
|
3535
|
+
* Warning: `listAll` may potentially consume too many resources if there are
|
|
3536
|
+
* too many results.
|
|
3537
|
+
* @public
|
|
3538
|
+
* @param ref - {@link StorageReference} to get list from.
|
|
3539
|
+
*
|
|
3540
|
+
* @returns A `Promise` that resolves with all the items and prefixes under
|
|
3541
|
+
* the current storage reference. `prefixes` contains references to
|
|
3542
|
+
* sub-directories and `items` contains references to objects in this
|
|
3543
|
+
* folder. `nextPageToken` is never returned.
|
|
3544
|
+
*/
|
|
3545
|
+
function listAll(ref) {
|
|
3546
|
+
ref = util.getModularInstance(ref);
|
|
3547
|
+
return listAll$1(ref);
|
|
3548
|
+
}
|
|
3549
|
+
/**
|
|
3550
|
+
* Returns the download URL for the given {@link StorageReference}.
|
|
3551
|
+
* @public
|
|
3552
|
+
* @param ref - {@link StorageReference} to get the download URL for.
|
|
3553
|
+
* @returns A `Promise` that resolves with the download
|
|
3554
|
+
* URL for this object.
|
|
3555
|
+
*/
|
|
3556
|
+
function getDownloadURL(ref) {
|
|
3557
|
+
ref = util.getModularInstance(ref);
|
|
3558
|
+
return getDownloadURL$1(ref);
|
|
3559
|
+
}
|
|
3560
|
+
/**
|
|
3561
|
+
* Deletes the object at this location.
|
|
3562
|
+
* @public
|
|
3563
|
+
* @param ref - {@link StorageReference} for object to delete.
|
|
3564
|
+
* @returns A `Promise` that resolves if the deletion succeeds.
|
|
3565
|
+
*/
|
|
3566
|
+
function deleteObject(ref) {
|
|
3567
|
+
ref = util.getModularInstance(ref);
|
|
3568
|
+
return deleteObject$1(ref);
|
|
3569
|
+
}
|
|
3570
|
+
function ref(serviceOrRef, pathOrUrl) {
|
|
3571
|
+
serviceOrRef = util.getModularInstance(serviceOrRef);
|
|
3572
|
+
return ref$1(serviceOrRef, pathOrUrl);
|
|
3573
|
+
}
|
|
3574
|
+
/**
|
|
3575
|
+
* @internal
|
|
3576
|
+
*/
|
|
3577
|
+
function _getChild(ref, childPath) {
|
|
3578
|
+
return _getChild$1(ref, childPath);
|
|
3579
|
+
}
|
|
3580
|
+
/**
|
|
3581
|
+
* Gets a {@link FirebaseStorage} instance for the given Firebase app.
|
|
3582
|
+
* @public
|
|
3583
|
+
* @param app - Firebase app to get {@link FirebaseStorage} instance for.
|
|
3584
|
+
* @param bucketUrl - The gs:// url to your Firebase Storage Bucket.
|
|
3585
|
+
* If not passed, uses the app's default Storage Bucket.
|
|
3586
|
+
* @returns A {@link FirebaseStorage} instance.
|
|
3587
|
+
*/
|
|
3588
|
+
function getStorage(app$1 = app.getApp(), bucketUrl) {
|
|
3589
|
+
app$1 = util.getModularInstance(app$1);
|
|
3590
|
+
const storageProvider = app._getProvider(app$1, STORAGE_TYPE);
|
|
3591
|
+
const storageInstance = storageProvider.getImmediate({
|
|
3592
|
+
identifier: bucketUrl
|
|
3593
|
+
});
|
|
3594
|
+
const emulator = util.getDefaultEmulatorHostnameAndPort('storage');
|
|
3595
|
+
if (emulator) {
|
|
3596
|
+
connectStorageEmulator(storageInstance, ...emulator);
|
|
3597
|
+
}
|
|
3598
|
+
return storageInstance;
|
|
3599
|
+
}
|
|
3600
|
+
/**
|
|
3601
|
+
* Modify this {@link FirebaseStorage} instance to communicate with the Cloud Storage emulator.
|
|
3602
|
+
*
|
|
3603
|
+
* @param storage - The {@link FirebaseStorage} instance
|
|
3604
|
+
* @param host - The emulator host (ex: localhost)
|
|
3605
|
+
* @param port - The emulator port (ex: 5001)
|
|
3606
|
+
* @param options - Emulator options. `options.mockUserToken` is the mock auth
|
|
3607
|
+
* token to use for unit testing Security Rules.
|
|
3608
|
+
* @public
|
|
3609
|
+
*/
|
|
3610
|
+
function connectStorageEmulator(storage, host, port, options = {}) {
|
|
3611
|
+
connectStorageEmulator$1(storage, host, port, options);
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3614
|
+
/**
|
|
3615
|
+
* @license
|
|
3616
|
+
* Copyright 2021 Google LLC
|
|
3617
|
+
*
|
|
3618
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3619
|
+
* you may not use this file except in compliance with the License.
|
|
3620
|
+
* You may obtain a copy of the License at
|
|
3621
|
+
*
|
|
3622
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3623
|
+
*
|
|
3624
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3625
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3626
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3627
|
+
* See the License for the specific language governing permissions and
|
|
3628
|
+
* limitations under the License.
|
|
3629
|
+
*/
|
|
3630
|
+
/**
|
|
3631
|
+
* Downloads the data at the object's location. Returns an error if the object
|
|
3632
|
+
* is not found.
|
|
3633
|
+
*
|
|
3634
|
+
* To use this functionality, you have to whitelist your app's origin in your
|
|
3635
|
+
* Cloud Storage bucket. See also
|
|
3636
|
+
* https://cloud.google.com/storage/docs/configuring-cors
|
|
3637
|
+
*
|
|
3638
|
+
* This API is not available in Node.
|
|
3639
|
+
*
|
|
3640
|
+
* @public
|
|
3641
|
+
* @param ref - StorageReference where data should be downloaded.
|
|
3642
|
+
* @param maxDownloadSizeBytes - If set, the maximum allowed size in bytes to
|
|
3643
|
+
* retrieve.
|
|
3644
|
+
* @returns A Promise that resolves with a Blob containing the object's bytes
|
|
3645
|
+
*/
|
|
3646
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3647
|
+
function getBlob(ref, maxDownloadSizeBytes) {
|
|
3648
|
+
throw new Error('getBlob() is only available in Browser-like environments');
|
|
3649
|
+
}
|
|
3650
|
+
/**
|
|
3651
|
+
* Downloads the data at the object's location. Raises an error event if the
|
|
3652
|
+
* object is not found.
|
|
3653
|
+
*
|
|
3654
|
+
* This API is only available in Node.
|
|
3655
|
+
*
|
|
3656
|
+
* @public
|
|
3657
|
+
* @param ref - StorageReference where data should be downloaded.
|
|
3658
|
+
* @param maxDownloadSizeBytes - If set, the maximum allowed size in bytes to
|
|
3659
|
+
* retrieve.
|
|
3660
|
+
* @returns A stream with the object's data as bytes
|
|
3661
|
+
*/
|
|
3662
|
+
function getStream(ref, maxDownloadSizeBytes) {
|
|
3663
|
+
ref = util.getModularInstance(ref);
|
|
3664
|
+
return getStreamInternal(ref, maxDownloadSizeBytes);
|
|
3665
|
+
}
|
|
3666
|
+
|
|
3667
|
+
/**
|
|
3668
|
+
* Cloud Storage for Firebase
|
|
3669
|
+
*
|
|
3670
|
+
* @packageDocumentation
|
|
3671
|
+
*/
|
|
3672
|
+
function factory(container, { instanceIdentifier: url }) {
|
|
3673
|
+
const app$1 = container.getProvider('app').getImmediate();
|
|
3674
|
+
const authProvider = container.getProvider('auth-internal');
|
|
3675
|
+
const appCheckProvider = container.getProvider('app-check-internal');
|
|
3676
|
+
return new FirebaseStorageImpl(app$1, authProvider, appCheckProvider, url, app.SDK_VERSION);
|
|
3677
|
+
}
|
|
3678
|
+
function registerStorage() {
|
|
3679
|
+
app._registerComponent(new component.Component(STORAGE_TYPE, factory, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
|
|
3680
|
+
app.registerVersion(name, version);
|
|
3681
|
+
}
|
|
3682
|
+
registerStorage();
|
|
3683
|
+
|
|
3684
|
+
exports.StorageError = StorageError;
|
|
3685
|
+
exports.StringFormat = StringFormat;
|
|
3686
|
+
exports._FbsBlob = FbsBlob;
|
|
3687
|
+
exports._Location = Location;
|
|
3688
|
+
exports._TaskEvent = TaskEvent;
|
|
3689
|
+
exports._TaskState = TaskState;
|
|
3690
|
+
exports._UploadTask = UploadTask;
|
|
3691
|
+
exports._dataFromString = dataFromString;
|
|
3692
|
+
exports._getChild = _getChild;
|
|
3693
|
+
exports._invalidArgument = invalidArgument;
|
|
3694
|
+
exports._invalidRootOperation = invalidRootOperation;
|
|
3695
|
+
exports.connectStorageEmulator = connectStorageEmulator;
|
|
3696
|
+
exports.deleteObject = deleteObject;
|
|
3697
|
+
exports.getBlob = getBlob;
|
|
3698
|
+
exports.getBytes = getBytes;
|
|
3699
|
+
exports.getDownloadURL = getDownloadURL;
|
|
3700
|
+
exports.getMetadata = getMetadata;
|
|
3701
|
+
exports.getStorage = getStorage;
|
|
3702
|
+
exports.getStream = getStream;
|
|
3703
|
+
exports.list = list;
|
|
3704
|
+
exports.listAll = listAll;
|
|
3705
|
+
exports.ref = ref;
|
|
3706
|
+
exports.updateMetadata = updateMetadata;
|
|
3707
|
+
exports.uploadBytes = uploadBytes;
|
|
3708
|
+
exports.uploadBytesResumable = uploadBytesResumable;
|
|
3709
|
+
exports.uploadString = uploadString;
|
|
3710
|
+
//# sourceMappingURL=index.node.cjs.js.map
|