@firebase/storage 0.9.10 → 0.9.11-20221010203322
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/CHANGELOG.md +14 -0
- package/dist/index.browser.cjs.js +125 -60
- package/dist/index.browser.cjs.js.map +1 -1
- package/dist/index.esm2017.js +117 -45
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm5.js +126 -61
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +116 -44
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/node-esm/index.node.esm.js +117 -45
- package/dist/node-esm/index.node.esm.js.map +1 -1
- package/dist/node-esm/src/implementation/backoff.d.ts +11 -5
- package/dist/node-esm/src/implementation/constants.d.ts +4 -0
- package/dist/node-esm/src/implementation/error.d.ts +5 -1
- package/dist/node-esm/src/implementation/request.d.ts +1 -1
- package/dist/node-esm/src/implementation/utils.d.ts +23 -0
- package/dist/node-esm/src/service.d.ts +1 -1
- package/dist/node-esm/src/task.d.ts +5 -1
- package/dist/node-esm/test/unit/testshared.d.ts +14 -1
- package/dist/node-esm/test/unit/utils.test.d.ts +17 -0
- package/dist/src/implementation/backoff.d.ts +11 -5
- package/dist/src/implementation/constants.d.ts +4 -0
- package/dist/src/implementation/error.d.ts +5 -1
- package/dist/src/implementation/request.d.ts +1 -1
- package/dist/src/implementation/utils.d.ts +23 -0
- package/dist/src/service.d.ts +1 -1
- package/dist/src/task.d.ts +5 -1
- package/dist/storage.d.ts +10 -3
- package/dist/test/unit/testshared.d.ts +14 -1
- package/dist/test/unit/utils.test.d.ts +17 -0
- package/package.json +6 -6
package/dist/index.esm2017.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getApp, _getProvider, _registerComponent, registerVersion, SDK_VERSION } from '@firebase/app';
|
|
2
|
-
import { FirebaseError, createMockUserToken, getModularInstance,
|
|
2
|
+
import { FirebaseError, createMockUserToken, getModularInstance, getDefaultEmulatorHostnameAndPort } from '@firebase/util';
|
|
3
3
|
import { Component } from '@firebase/component';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -40,7 +40,11 @@ const DEFAULT_MAX_OPERATION_RETRY_TIME = 2 * 60 * 1000;
|
|
|
40
40
|
*
|
|
41
41
|
* The timeout for upload.
|
|
42
42
|
*/
|
|
43
|
-
const DEFAULT_MAX_UPLOAD_RETRY_TIME = 10 * 60 * 1000;
|
|
43
|
+
const DEFAULT_MAX_UPLOAD_RETRY_TIME = 10 * 60 * 1000;
|
|
44
|
+
/**
|
|
45
|
+
* 1 second
|
|
46
|
+
*/
|
|
47
|
+
const DEFAULT_MIN_SLEEP_TIME_MILLIS = 1000;
|
|
44
48
|
|
|
45
49
|
/**
|
|
46
50
|
* @license
|
|
@@ -67,9 +71,11 @@ class StorageError extends FirebaseError {
|
|
|
67
71
|
* @param code - A StorageErrorCode string to be prefixed with 'storage/' and
|
|
68
72
|
* added to the end of the message.
|
|
69
73
|
* @param message - Error message.
|
|
74
|
+
* @param status_ - Corresponding HTTP Status Code
|
|
70
75
|
*/
|
|
71
|
-
constructor(code, message) {
|
|
76
|
+
constructor(code, message, status_ = 0) {
|
|
72
77
|
super(prependCode(code), `Firebase Storage: ${message} (${prependCode(code)})`);
|
|
78
|
+
this.status_ = status_;
|
|
73
79
|
/**
|
|
74
80
|
* Stores custom error data unque to StorageError.
|
|
75
81
|
*/
|
|
@@ -79,6 +85,12 @@ class StorageError extends FirebaseError {
|
|
|
79
85
|
// returns false.
|
|
80
86
|
Object.setPrototypeOf(this, StorageError.prototype);
|
|
81
87
|
}
|
|
88
|
+
get status() {
|
|
89
|
+
return this.status_;
|
|
90
|
+
}
|
|
91
|
+
set status(status) {
|
|
92
|
+
this.status_ = status;
|
|
93
|
+
}
|
|
82
94
|
/**
|
|
83
95
|
* Compares a StorageErrorCode against this error's code, filtering out the prefix.
|
|
84
96
|
*/
|
|
@@ -338,14 +350,20 @@ class FailRequest {
|
|
|
338
350
|
* limitations under the License.
|
|
339
351
|
*/
|
|
340
352
|
/**
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
353
|
+
* Accepts a callback for an action to perform (`doRequest`),
|
|
354
|
+
* and then a callback for when the backoff has completed (`backoffCompleteCb`).
|
|
355
|
+
* The callback sent to start requires an argument to call (`onRequestComplete`).
|
|
356
|
+
* When `start` calls `doRequest`, it passes a callback for when the request has
|
|
357
|
+
* completed, `onRequestComplete`. Based on this, the backoff continues, with
|
|
358
|
+
* another call to `doRequest` and the above loop continues until the timeout
|
|
359
|
+
* is hit, or a successful response occurs.
|
|
360
|
+
* @description
|
|
361
|
+
* @param doRequest Callback to perform request
|
|
362
|
+
* @param backoffCompleteCb Callback to call when backoff has been completed
|
|
345
363
|
*/
|
|
346
|
-
function start(
|
|
364
|
+
function start(doRequest,
|
|
347
365
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
348
|
-
|
|
366
|
+
backoffCompleteCb, timeout) {
|
|
349
367
|
// TODO(andysoto): make this code cleaner (probably refactor into an actual
|
|
350
368
|
// type instead of a bunch of functions with state shared in the closure)
|
|
351
369
|
let waitSeconds = 1;
|
|
@@ -364,13 +382,13 @@ callback, timeout) {
|
|
|
364
382
|
function triggerCallback(...args) {
|
|
365
383
|
if (!triggeredCallback) {
|
|
366
384
|
triggeredCallback = true;
|
|
367
|
-
|
|
385
|
+
backoffCompleteCb.apply(null, args);
|
|
368
386
|
}
|
|
369
387
|
}
|
|
370
388
|
function callWithDelay(millis) {
|
|
371
389
|
retryTimeoutId = setTimeout(() => {
|
|
372
390
|
retryTimeoutId = null;
|
|
373
|
-
|
|
391
|
+
doRequest(responseHandler, canceled());
|
|
374
392
|
}, millis);
|
|
375
393
|
}
|
|
376
394
|
function clearGlobalTimeout() {
|
|
@@ -378,7 +396,7 @@ callback, timeout) {
|
|
|
378
396
|
clearTimeout(globalTimeoutId);
|
|
379
397
|
}
|
|
380
398
|
}
|
|
381
|
-
function
|
|
399
|
+
function responseHandler(success, ...args) {
|
|
382
400
|
if (triggeredCallback) {
|
|
383
401
|
clearGlobalTimeout();
|
|
384
402
|
return;
|
|
@@ -556,6 +574,43 @@ var ErrorCode;
|
|
|
556
574
|
ErrorCode[ErrorCode["ABORT"] = 2] = "ABORT";
|
|
557
575
|
})(ErrorCode || (ErrorCode = {}));
|
|
558
576
|
|
|
577
|
+
/**
|
|
578
|
+
* @license
|
|
579
|
+
* Copyright 2022 Google LLC
|
|
580
|
+
*
|
|
581
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
582
|
+
* you may not use this file except in compliance with the License.
|
|
583
|
+
* You may obtain a copy of the License at
|
|
584
|
+
*
|
|
585
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
586
|
+
*
|
|
587
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
588
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
589
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
590
|
+
* See the License for the specific language governing permissions and
|
|
591
|
+
* limitations under the License.
|
|
592
|
+
*/
|
|
593
|
+
/**
|
|
594
|
+
* Checks the status code to see if the action should be retried.
|
|
595
|
+
*
|
|
596
|
+
* @param status Current HTTP status code returned by server.
|
|
597
|
+
* @param additionalRetryCodes additional retry codes to check against
|
|
598
|
+
*/
|
|
599
|
+
function isRetryStatusCode(status, additionalRetryCodes) {
|
|
600
|
+
// The codes for which to retry came from this page:
|
|
601
|
+
// https://cloud.google.com/storage/docs/exponential-backoff
|
|
602
|
+
const isFiveHundredCode = status >= 500 && status < 600;
|
|
603
|
+
const extraRetryCodes = [
|
|
604
|
+
// Request Timeout: web server didn't receive full request in time.
|
|
605
|
+
408,
|
|
606
|
+
// Too Many Requests: you're getting rate-limited, basically.
|
|
607
|
+
429
|
|
608
|
+
];
|
|
609
|
+
const isExtraRetryCode = extraRetryCodes.indexOf(status) !== -1;
|
|
610
|
+
const isAdditionalRetryCode = additionalRetryCodes.indexOf(status) !== -1;
|
|
611
|
+
return isFiveHundredCode || isExtraRetryCode || isAdditionalRetryCode;
|
|
612
|
+
}
|
|
613
|
+
|
|
559
614
|
/**
|
|
560
615
|
* @license
|
|
561
616
|
* Copyright 2017 Google LLC
|
|
@@ -581,7 +636,7 @@ var ErrorCode;
|
|
|
581
636
|
* happens in the specified `callback_`.
|
|
582
637
|
*/
|
|
583
638
|
class NetworkRequest {
|
|
584
|
-
constructor(url_, method_, headers_, body_, successCodes_, additionalRetryCodes_, callback_, errorCallback_, timeout_, progressCallback_, connectionFactory_) {
|
|
639
|
+
constructor(url_, method_, headers_, body_, successCodes_, additionalRetryCodes_, callback_, errorCallback_, timeout_, progressCallback_, connectionFactory_, retry = true) {
|
|
585
640
|
this.url_ = url_;
|
|
586
641
|
this.method_ = method_;
|
|
587
642
|
this.headers_ = headers_;
|
|
@@ -593,6 +648,7 @@ class NetworkRequest {
|
|
|
593
648
|
this.timeout_ = timeout_;
|
|
594
649
|
this.progressCallback_ = progressCallback_;
|
|
595
650
|
this.connectionFactory_ = connectionFactory_;
|
|
651
|
+
this.retry = retry;
|
|
596
652
|
this.pendingConnection_ = null;
|
|
597
653
|
this.backoffId_ = null;
|
|
598
654
|
this.canceled_ = false;
|
|
@@ -616,9 +672,7 @@ class NetworkRequest {
|
|
|
616
672
|
this.pendingConnection_ = connection;
|
|
617
673
|
const progressListener = progressEvent => {
|
|
618
674
|
const loaded = progressEvent.loaded;
|
|
619
|
-
const total = progressEvent.lengthComputable
|
|
620
|
-
? progressEvent.total
|
|
621
|
-
: -1;
|
|
675
|
+
const total = progressEvent.lengthComputable ? progressEvent.total : -1;
|
|
622
676
|
if (this.progressCallback_ !== null) {
|
|
623
677
|
this.progressCallback_(loaded, total);
|
|
624
678
|
}
|
|
@@ -637,7 +691,9 @@ class NetworkRequest {
|
|
|
637
691
|
this.pendingConnection_ = null;
|
|
638
692
|
const hitServer = connection.getErrorCode() === ErrorCode.NO_ERROR;
|
|
639
693
|
const status = connection.getStatus();
|
|
640
|
-
if (!hitServer ||
|
|
694
|
+
if ((!hitServer ||
|
|
695
|
+
isRetryStatusCode(status, this.additionalRetryCodes_)) &&
|
|
696
|
+
this.retry) {
|
|
641
697
|
const wasCanceled = connection.getErrorCode() === ErrorCode.ABORT;
|
|
642
698
|
backoffCallback(false, new RequestEndStatus(false, null, wasCanceled));
|
|
643
699
|
return;
|
|
@@ -713,20 +769,6 @@ class NetworkRequest {
|
|
|
713
769
|
this.pendingConnection_.abort();
|
|
714
770
|
}
|
|
715
771
|
}
|
|
716
|
-
isRetryStatusCode_(status) {
|
|
717
|
-
// The codes for which to retry came from this page:
|
|
718
|
-
// https://cloud.google.com/storage/docs/exponential-backoff
|
|
719
|
-
const isFiveHundredCode = status >= 500 && status < 600;
|
|
720
|
-
const extraRetryCodes = [
|
|
721
|
-
// Request Timeout: web server didn't receive full request in time.
|
|
722
|
-
408,
|
|
723
|
-
// Too Many Requests: you're getting rate-limited, basically.
|
|
724
|
-
429
|
|
725
|
-
];
|
|
726
|
-
const isExtraRetryCode = extraRetryCodes.indexOf(status) !== -1;
|
|
727
|
-
const isRequestSpecificRetryCode = this.additionalRetryCodes_.indexOf(status) !== -1;
|
|
728
|
-
return isFiveHundredCode || isExtraRetryCode || isRequestSpecificRetryCode;
|
|
729
|
-
}
|
|
730
772
|
}
|
|
731
773
|
/**
|
|
732
774
|
* A collection of information about the result of a network request.
|
|
@@ -758,7 +800,7 @@ function addAppCheckHeader_(headers, appCheckToken) {
|
|
|
758
800
|
headers['X-Firebase-AppCheck'] = appCheckToken;
|
|
759
801
|
}
|
|
760
802
|
}
|
|
761
|
-
function makeRequest(requestInfo, appId, authToken, appCheckToken, requestFactory, firebaseVersion) {
|
|
803
|
+
function makeRequest(requestInfo, appId, authToken, appCheckToken, requestFactory, firebaseVersion, retry = true) {
|
|
762
804
|
const queryPart = makeQueryString(requestInfo.urlParams);
|
|
763
805
|
const url = requestInfo.url + queryPart;
|
|
764
806
|
const headers = Object.assign({}, requestInfo.headers);
|
|
@@ -766,7 +808,7 @@ function makeRequest(requestInfo, appId, authToken, appCheckToken, requestFactor
|
|
|
766
808
|
addAuthHeader_(headers, authToken);
|
|
767
809
|
addVersionHeader_(headers, firebaseVersion);
|
|
768
810
|
addAppCheckHeader_(headers, appCheckToken);
|
|
769
|
-
return new NetworkRequest(url, requestInfo.method, headers, requestInfo.body, requestInfo.successCodes, requestInfo.additionalRetryCodes, requestInfo.handler, requestInfo.errorHandler, requestInfo.timeout, requestInfo.progressCallback, requestFactory);
|
|
811
|
+
return new NetworkRequest(url, requestInfo.method, headers, requestInfo.body, requestInfo.successCodes, requestInfo.additionalRetryCodes, requestInfo.handler, requestInfo.errorHandler, requestInfo.timeout, requestInfo.progressCallback, requestFactory, retry);
|
|
770
812
|
}
|
|
771
813
|
|
|
772
814
|
/**
|
|
@@ -1583,6 +1625,7 @@ function sharedErrorHandler(location) {
|
|
|
1583
1625
|
}
|
|
1584
1626
|
}
|
|
1585
1627
|
}
|
|
1628
|
+
newErr.status = xhr.getStatus();
|
|
1586
1629
|
newErr.serverResponse = err.serverResponse;
|
|
1587
1630
|
return newErr;
|
|
1588
1631
|
}
|
|
@@ -1867,7 +1910,16 @@ function continueResumableUpload(location, service, url, blob, chunkSize, mappin
|
|
|
1867
1910
|
}
|
|
1868
1911
|
const startByte = status_.current;
|
|
1869
1912
|
const endByte = startByte + bytesToUpload;
|
|
1870
|
-
|
|
1913
|
+
let uploadCommand = '';
|
|
1914
|
+
if (bytesToUpload === 0) {
|
|
1915
|
+
uploadCommand = 'finalize';
|
|
1916
|
+
}
|
|
1917
|
+
else if (bytesLeft === bytesToUpload) {
|
|
1918
|
+
uploadCommand = 'upload, finalize';
|
|
1919
|
+
}
|
|
1920
|
+
else {
|
|
1921
|
+
uploadCommand = 'upload';
|
|
1922
|
+
}
|
|
1871
1923
|
const headers = {
|
|
1872
1924
|
'X-Goog-Upload-Command': uploadCommand,
|
|
1873
1925
|
'X-Goog-Upload-Offset': `${status_.current}`
|
|
@@ -2227,6 +2279,18 @@ class UploadTask {
|
|
|
2227
2279
|
this.completeTransitions_();
|
|
2228
2280
|
}
|
|
2229
2281
|
else {
|
|
2282
|
+
const backoffExpired = this.isExponentialBackoffExpired();
|
|
2283
|
+
if (isRetryStatusCode(error.status, [])) {
|
|
2284
|
+
if (backoffExpired) {
|
|
2285
|
+
error = retryLimitExceeded();
|
|
2286
|
+
}
|
|
2287
|
+
else {
|
|
2288
|
+
this.sleepTime = Math.max(this.sleepTime * 2, DEFAULT_MIN_SLEEP_TIME_MILLIS);
|
|
2289
|
+
this._needToFetchStatus = true;
|
|
2290
|
+
this.completeTransitions_();
|
|
2291
|
+
return;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2230
2294
|
this._error = error;
|
|
2231
2295
|
this._transition("error" /* ERROR */);
|
|
2232
2296
|
}
|
|
@@ -2241,6 +2305,8 @@ class UploadTask {
|
|
|
2241
2305
|
this._transition("error" /* ERROR */);
|
|
2242
2306
|
}
|
|
2243
2307
|
};
|
|
2308
|
+
this.sleepTime = 0;
|
|
2309
|
+
this.maxSleepTime = this._ref.storage.maxUploadRetryTime;
|
|
2244
2310
|
this._promise = new Promise((resolve, reject) => {
|
|
2245
2311
|
this._resolve = resolve;
|
|
2246
2312
|
this._reject = reject;
|
|
@@ -2250,6 +2316,9 @@ class UploadTask {
|
|
|
2250
2316
|
// to the top level with a dummy handler.
|
|
2251
2317
|
this._promise.then(null, () => { });
|
|
2252
2318
|
}
|
|
2319
|
+
isExponentialBackoffExpired() {
|
|
2320
|
+
return this.sleepTime > this.maxSleepTime;
|
|
2321
|
+
}
|
|
2253
2322
|
_makeProgressCallback() {
|
|
2254
2323
|
const sizeBefore = this._transferred;
|
|
2255
2324
|
return loaded => this._updateProgress(sizeBefore + loaded);
|
|
@@ -2279,7 +2348,9 @@ class UploadTask {
|
|
|
2279
2348
|
this._fetchMetadata();
|
|
2280
2349
|
}
|
|
2281
2350
|
else {
|
|
2282
|
-
|
|
2351
|
+
setTimeout(() => {
|
|
2352
|
+
this._continueUpload();
|
|
2353
|
+
}, this.sleepTime);
|
|
2283
2354
|
}
|
|
2284
2355
|
}
|
|
2285
2356
|
}
|
|
@@ -2355,7 +2426,9 @@ class UploadTask {
|
|
|
2355
2426
|
this._transition("error" /* ERROR */);
|
|
2356
2427
|
return;
|
|
2357
2428
|
}
|
|
2358
|
-
const uploadRequest = this._ref.storage._makeRequest(requestInfo, newTextConnection, authToken, appCheckToken
|
|
2429
|
+
const uploadRequest = this._ref.storage._makeRequest(requestInfo, newTextConnection, authToken, appCheckToken,
|
|
2430
|
+
/*retry=*/ false // Upload requests should not be retried as each retry should be preceded by another query request. Which is handled in this file.
|
|
2431
|
+
);
|
|
2359
2432
|
this._request = uploadRequest;
|
|
2360
2433
|
uploadRequest.getPromise().then((newStatus) => {
|
|
2361
2434
|
this._increaseMultiplier();
|
|
@@ -2374,7 +2447,7 @@ class UploadTask {
|
|
|
2374
2447
|
_increaseMultiplier() {
|
|
2375
2448
|
const currentSize = RESUMABLE_UPLOAD_CHUNK_SIZE * this._chunkMultiplier;
|
|
2376
2449
|
// Max chunk size is 32M.
|
|
2377
|
-
if (currentSize < 32 * 1024 * 1024) {
|
|
2450
|
+
if (currentSize * 2 < 32 * 1024 * 1024) {
|
|
2378
2451
|
this._chunkMultiplier *= 2;
|
|
2379
2452
|
}
|
|
2380
2453
|
}
|
|
@@ -2523,6 +2596,7 @@ class UploadTask {
|
|
|
2523
2596
|
* callbacks.
|
|
2524
2597
|
*/
|
|
2525
2598
|
on(type, nextOrObserver, error, completed) {
|
|
2599
|
+
// Note: `type` isn't being used. Its type is also incorrect. TaskEvent should not be a string.
|
|
2526
2600
|
const observer = new Observer(nextOrObserver || undefined, error || undefined, completed || undefined);
|
|
2527
2601
|
this._addObserver(observer);
|
|
2528
2602
|
return () => {
|
|
@@ -3208,9 +3282,9 @@ class FirebaseStorageImpl {
|
|
|
3208
3282
|
* @param requestInfo - HTTP RequestInfo object
|
|
3209
3283
|
* @param authToken - Firebase auth token
|
|
3210
3284
|
*/
|
|
3211
|
-
_makeRequest(requestInfo, requestFactory, authToken, appCheckToken) {
|
|
3285
|
+
_makeRequest(requestInfo, requestFactory, authToken, appCheckToken, retry = true) {
|
|
3212
3286
|
if (!this._deleted) {
|
|
3213
|
-
const request = makeRequest(requestInfo, this._appId, authToken, appCheckToken, requestFactory, this._firebaseVersion);
|
|
3287
|
+
const request = makeRequest(requestInfo, this._appId, authToken, appCheckToken, requestFactory, this._firebaseVersion, retry);
|
|
3214
3288
|
this._requests.add(request);
|
|
3215
3289
|
// Request removes itself from set when complete.
|
|
3216
3290
|
request.getPromise().then(() => this._requests.delete(request), () => this._requests.delete(request));
|
|
@@ -3230,7 +3304,7 @@ class FirebaseStorageImpl {
|
|
|
3230
3304
|
}
|
|
3231
3305
|
|
|
3232
3306
|
const name = "@firebase/storage";
|
|
3233
|
-
const version = "0.9.
|
|
3307
|
+
const version = "0.9.11-20221010203322";
|
|
3234
3308
|
|
|
3235
3309
|
/**
|
|
3236
3310
|
* @license
|
|
@@ -3445,11 +3519,9 @@ function getStorage(app = getApp(), bucketUrl) {
|
|
|
3445
3519
|
const storageInstance = storageProvider.getImmediate({
|
|
3446
3520
|
identifier: bucketUrl
|
|
3447
3521
|
});
|
|
3448
|
-
const
|
|
3449
|
-
if (
|
|
3450
|
-
|
|
3451
|
-
// eslint-disable-next-line no-restricted-globals
|
|
3452
|
-
connectStorageEmulator(storageInstance, host, parseInt(port, 10));
|
|
3522
|
+
const emulator = getDefaultEmulatorHostnameAndPort('storage');
|
|
3523
|
+
if (emulator) {
|
|
3524
|
+
connectStorageEmulator(storageInstance, ...emulator);
|
|
3453
3525
|
}
|
|
3454
3526
|
return storageInstance;
|
|
3455
3527
|
}
|