@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
|
@@ -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 { Transform, PassThrough } from 'stream';
|
|
4
4
|
import nodeFetch from 'node-fetch';
|
|
5
5
|
import { Component } from '@firebase/component';
|
|
@@ -42,7 +42,11 @@ const DEFAULT_MAX_OPERATION_RETRY_TIME = 2 * 60 * 1000;
|
|
|
42
42
|
*
|
|
43
43
|
* The timeout for upload.
|
|
44
44
|
*/
|
|
45
|
-
const DEFAULT_MAX_UPLOAD_RETRY_TIME = 10 * 60 * 1000;
|
|
45
|
+
const DEFAULT_MAX_UPLOAD_RETRY_TIME = 10 * 60 * 1000;
|
|
46
|
+
/**
|
|
47
|
+
* 1 second
|
|
48
|
+
*/
|
|
49
|
+
const DEFAULT_MIN_SLEEP_TIME_MILLIS = 1000;
|
|
46
50
|
|
|
47
51
|
/**
|
|
48
52
|
* @license
|
|
@@ -69,9 +73,11 @@ class StorageError extends FirebaseError {
|
|
|
69
73
|
* @param code - A StorageErrorCode string to be prefixed with 'storage/' and
|
|
70
74
|
* added to the end of the message.
|
|
71
75
|
* @param message - Error message.
|
|
76
|
+
* @param status_ - Corresponding HTTP Status Code
|
|
72
77
|
*/
|
|
73
|
-
constructor(code, message) {
|
|
78
|
+
constructor(code, message, status_ = 0) {
|
|
74
79
|
super(prependCode(code), `Firebase Storage: ${message} (${prependCode(code)})`);
|
|
80
|
+
this.status_ = status_;
|
|
75
81
|
/**
|
|
76
82
|
* Stores custom error data unque to StorageError.
|
|
77
83
|
*/
|
|
@@ -81,6 +87,12 @@ class StorageError extends FirebaseError {
|
|
|
81
87
|
// returns false.
|
|
82
88
|
Object.setPrototypeOf(this, StorageError.prototype);
|
|
83
89
|
}
|
|
90
|
+
get status() {
|
|
91
|
+
return this.status_;
|
|
92
|
+
}
|
|
93
|
+
set status(status) {
|
|
94
|
+
this.status_ = status;
|
|
95
|
+
}
|
|
84
96
|
/**
|
|
85
97
|
* Compares a StorageErrorCode against this error's code, filtering out the prefix.
|
|
86
98
|
*/
|
|
@@ -340,14 +352,20 @@ class FailRequest {
|
|
|
340
352
|
* limitations under the License.
|
|
341
353
|
*/
|
|
342
354
|
/**
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
355
|
+
* Accepts a callback for an action to perform (`doRequest`),
|
|
356
|
+
* and then a callback for when the backoff has completed (`backoffCompleteCb`).
|
|
357
|
+
* The callback sent to start requires an argument to call (`onRequestComplete`).
|
|
358
|
+
* When `start` calls `doRequest`, it passes a callback for when the request has
|
|
359
|
+
* completed, `onRequestComplete`. Based on this, the backoff continues, with
|
|
360
|
+
* another call to `doRequest` and the above loop continues until the timeout
|
|
361
|
+
* is hit, or a successful response occurs.
|
|
362
|
+
* @description
|
|
363
|
+
* @param doRequest Callback to perform request
|
|
364
|
+
* @param backoffCompleteCb Callback to call when backoff has been completed
|
|
347
365
|
*/
|
|
348
|
-
function start(
|
|
366
|
+
function start(doRequest,
|
|
349
367
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
350
|
-
|
|
368
|
+
backoffCompleteCb, timeout) {
|
|
351
369
|
// TODO(andysoto): make this code cleaner (probably refactor into an actual
|
|
352
370
|
// type instead of a bunch of functions with state shared in the closure)
|
|
353
371
|
let waitSeconds = 1;
|
|
@@ -366,13 +384,13 @@ callback, timeout) {
|
|
|
366
384
|
function triggerCallback(...args) {
|
|
367
385
|
if (!triggeredCallback) {
|
|
368
386
|
triggeredCallback = true;
|
|
369
|
-
|
|
387
|
+
backoffCompleteCb.apply(null, args);
|
|
370
388
|
}
|
|
371
389
|
}
|
|
372
390
|
function callWithDelay(millis) {
|
|
373
391
|
retryTimeoutId = setTimeout(() => {
|
|
374
392
|
retryTimeoutId = null;
|
|
375
|
-
|
|
393
|
+
doRequest(responseHandler, canceled());
|
|
376
394
|
}, millis);
|
|
377
395
|
}
|
|
378
396
|
function clearGlobalTimeout() {
|
|
@@ -380,7 +398,7 @@ callback, timeout) {
|
|
|
380
398
|
clearTimeout(globalTimeoutId);
|
|
381
399
|
}
|
|
382
400
|
}
|
|
383
|
-
function
|
|
401
|
+
function responseHandler(success, ...args) {
|
|
384
402
|
if (triggeredCallback) {
|
|
385
403
|
clearGlobalTimeout();
|
|
386
404
|
return;
|
|
@@ -558,6 +576,43 @@ var ErrorCode;
|
|
|
558
576
|
ErrorCode[ErrorCode["ABORT"] = 2] = "ABORT";
|
|
559
577
|
})(ErrorCode || (ErrorCode = {}));
|
|
560
578
|
|
|
579
|
+
/**
|
|
580
|
+
* @license
|
|
581
|
+
* Copyright 2022 Google LLC
|
|
582
|
+
*
|
|
583
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
584
|
+
* you may not use this file except in compliance with the License.
|
|
585
|
+
* You may obtain a copy of the License at
|
|
586
|
+
*
|
|
587
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
588
|
+
*
|
|
589
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
590
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
591
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
592
|
+
* See the License for the specific language governing permissions and
|
|
593
|
+
* limitations under the License.
|
|
594
|
+
*/
|
|
595
|
+
/**
|
|
596
|
+
* Checks the status code to see if the action should be retried.
|
|
597
|
+
*
|
|
598
|
+
* @param status Current HTTP status code returned by server.
|
|
599
|
+
* @param additionalRetryCodes additional retry codes to check against
|
|
600
|
+
*/
|
|
601
|
+
function isRetryStatusCode(status, additionalRetryCodes) {
|
|
602
|
+
// The codes for which to retry came from this page:
|
|
603
|
+
// https://cloud.google.com/storage/docs/exponential-backoff
|
|
604
|
+
const isFiveHundredCode = status >= 500 && status < 600;
|
|
605
|
+
const extraRetryCodes = [
|
|
606
|
+
// Request Timeout: web server didn't receive full request in time.
|
|
607
|
+
408,
|
|
608
|
+
// Too Many Requests: you're getting rate-limited, basically.
|
|
609
|
+
429
|
|
610
|
+
];
|
|
611
|
+
const isExtraRetryCode = extraRetryCodes.indexOf(status) !== -1;
|
|
612
|
+
const isAdditionalRetryCode = additionalRetryCodes.indexOf(status) !== -1;
|
|
613
|
+
return isFiveHundredCode || isExtraRetryCode || isAdditionalRetryCode;
|
|
614
|
+
}
|
|
615
|
+
|
|
561
616
|
/**
|
|
562
617
|
* @license
|
|
563
618
|
* Copyright 2017 Google LLC
|
|
@@ -583,7 +638,7 @@ var ErrorCode;
|
|
|
583
638
|
* happens in the specified `callback_`.
|
|
584
639
|
*/
|
|
585
640
|
class NetworkRequest {
|
|
586
|
-
constructor(url_, method_, headers_, body_, successCodes_, additionalRetryCodes_, callback_, errorCallback_, timeout_, progressCallback_, connectionFactory_) {
|
|
641
|
+
constructor(url_, method_, headers_, body_, successCodes_, additionalRetryCodes_, callback_, errorCallback_, timeout_, progressCallback_, connectionFactory_, retry = true) {
|
|
587
642
|
this.url_ = url_;
|
|
588
643
|
this.method_ = method_;
|
|
589
644
|
this.headers_ = headers_;
|
|
@@ -595,6 +650,7 @@ class NetworkRequest {
|
|
|
595
650
|
this.timeout_ = timeout_;
|
|
596
651
|
this.progressCallback_ = progressCallback_;
|
|
597
652
|
this.connectionFactory_ = connectionFactory_;
|
|
653
|
+
this.retry = retry;
|
|
598
654
|
this.pendingConnection_ = null;
|
|
599
655
|
this.backoffId_ = null;
|
|
600
656
|
this.canceled_ = false;
|
|
@@ -618,9 +674,7 @@ class NetworkRequest {
|
|
|
618
674
|
this.pendingConnection_ = connection;
|
|
619
675
|
const progressListener = progressEvent => {
|
|
620
676
|
const loaded = progressEvent.loaded;
|
|
621
|
-
const total = progressEvent.lengthComputable
|
|
622
|
-
? progressEvent.total
|
|
623
|
-
: -1;
|
|
677
|
+
const total = progressEvent.lengthComputable ? progressEvent.total : -1;
|
|
624
678
|
if (this.progressCallback_ !== null) {
|
|
625
679
|
this.progressCallback_(loaded, total);
|
|
626
680
|
}
|
|
@@ -639,7 +693,9 @@ class NetworkRequest {
|
|
|
639
693
|
this.pendingConnection_ = null;
|
|
640
694
|
const hitServer = connection.getErrorCode() === ErrorCode.NO_ERROR;
|
|
641
695
|
const status = connection.getStatus();
|
|
642
|
-
if (!hitServer ||
|
|
696
|
+
if ((!hitServer ||
|
|
697
|
+
isRetryStatusCode(status, this.additionalRetryCodes_)) &&
|
|
698
|
+
this.retry) {
|
|
643
699
|
const wasCanceled = connection.getErrorCode() === ErrorCode.ABORT;
|
|
644
700
|
backoffCallback(false, new RequestEndStatus(false, null, wasCanceled));
|
|
645
701
|
return;
|
|
@@ -715,20 +771,6 @@ class NetworkRequest {
|
|
|
715
771
|
this.pendingConnection_.abort();
|
|
716
772
|
}
|
|
717
773
|
}
|
|
718
|
-
isRetryStatusCode_(status) {
|
|
719
|
-
// The codes for which to retry came from this page:
|
|
720
|
-
// https://cloud.google.com/storage/docs/exponential-backoff
|
|
721
|
-
const isFiveHundredCode = status >= 500 && status < 600;
|
|
722
|
-
const extraRetryCodes = [
|
|
723
|
-
// Request Timeout: web server didn't receive full request in time.
|
|
724
|
-
408,
|
|
725
|
-
// Too Many Requests: you're getting rate-limited, basically.
|
|
726
|
-
429
|
|
727
|
-
];
|
|
728
|
-
const isExtraRetryCode = extraRetryCodes.indexOf(status) !== -1;
|
|
729
|
-
const isRequestSpecificRetryCode = this.additionalRetryCodes_.indexOf(status) !== -1;
|
|
730
|
-
return isFiveHundredCode || isExtraRetryCode || isRequestSpecificRetryCode;
|
|
731
|
-
}
|
|
732
774
|
}
|
|
733
775
|
/**
|
|
734
776
|
* A collection of information about the result of a network request.
|
|
@@ -760,7 +802,7 @@ function addAppCheckHeader_(headers, appCheckToken) {
|
|
|
760
802
|
headers['X-Firebase-AppCheck'] = appCheckToken;
|
|
761
803
|
}
|
|
762
804
|
}
|
|
763
|
-
function makeRequest(requestInfo, appId, authToken, appCheckToken, requestFactory, firebaseVersion) {
|
|
805
|
+
function makeRequest(requestInfo, appId, authToken, appCheckToken, requestFactory, firebaseVersion, retry = true) {
|
|
764
806
|
const queryPart = makeQueryString(requestInfo.urlParams);
|
|
765
807
|
const url = requestInfo.url + queryPart;
|
|
766
808
|
const headers = Object.assign({}, requestInfo.headers);
|
|
@@ -768,7 +810,7 @@ function makeRequest(requestInfo, appId, authToken, appCheckToken, requestFactor
|
|
|
768
810
|
addAuthHeader_(headers, authToken);
|
|
769
811
|
addVersionHeader_(headers, firebaseVersion);
|
|
770
812
|
addAppCheckHeader_(headers, appCheckToken);
|
|
771
|
-
return new NetworkRequest(url, requestInfo.method, headers, requestInfo.body, requestInfo.successCodes, requestInfo.additionalRetryCodes, requestInfo.handler, requestInfo.errorHandler, requestInfo.timeout, requestInfo.progressCallback, requestFactory);
|
|
813
|
+
return new NetworkRequest(url, requestInfo.method, headers, requestInfo.body, requestInfo.successCodes, requestInfo.additionalRetryCodes, requestInfo.handler, requestInfo.errorHandler, requestInfo.timeout, requestInfo.progressCallback, requestFactory, retry);
|
|
772
814
|
}
|
|
773
815
|
|
|
774
816
|
/**
|
|
@@ -1590,6 +1632,7 @@ function sharedErrorHandler(location) {
|
|
|
1590
1632
|
}
|
|
1591
1633
|
}
|
|
1592
1634
|
}
|
|
1635
|
+
newErr.status = xhr.getStatus();
|
|
1593
1636
|
newErr.serverResponse = err.serverResponse;
|
|
1594
1637
|
return newErr;
|
|
1595
1638
|
}
|
|
@@ -1874,7 +1917,16 @@ function continueResumableUpload(location, service, url, blob, chunkSize, mappin
|
|
|
1874
1917
|
}
|
|
1875
1918
|
const startByte = status_.current;
|
|
1876
1919
|
const endByte = startByte + bytesToUpload;
|
|
1877
|
-
|
|
1920
|
+
let uploadCommand = '';
|
|
1921
|
+
if (bytesToUpload === 0) {
|
|
1922
|
+
uploadCommand = 'finalize';
|
|
1923
|
+
}
|
|
1924
|
+
else if (bytesLeft === bytesToUpload) {
|
|
1925
|
+
uploadCommand = 'upload, finalize';
|
|
1926
|
+
}
|
|
1927
|
+
else {
|
|
1928
|
+
uploadCommand = 'upload';
|
|
1929
|
+
}
|
|
1878
1930
|
const headers = {
|
|
1879
1931
|
'X-Goog-Upload-Command': uploadCommand,
|
|
1880
1932
|
'X-Goog-Upload-Offset': `${status_.current}`
|
|
@@ -2249,6 +2301,18 @@ class UploadTask {
|
|
|
2249
2301
|
this.completeTransitions_();
|
|
2250
2302
|
}
|
|
2251
2303
|
else {
|
|
2304
|
+
const backoffExpired = this.isExponentialBackoffExpired();
|
|
2305
|
+
if (isRetryStatusCode(error.status, [])) {
|
|
2306
|
+
if (backoffExpired) {
|
|
2307
|
+
error = retryLimitExceeded();
|
|
2308
|
+
}
|
|
2309
|
+
else {
|
|
2310
|
+
this.sleepTime = Math.max(this.sleepTime * 2, DEFAULT_MIN_SLEEP_TIME_MILLIS);
|
|
2311
|
+
this._needToFetchStatus = true;
|
|
2312
|
+
this.completeTransitions_();
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2252
2316
|
this._error = error;
|
|
2253
2317
|
this._transition("error" /* ERROR */);
|
|
2254
2318
|
}
|
|
@@ -2263,6 +2327,8 @@ class UploadTask {
|
|
|
2263
2327
|
this._transition("error" /* ERROR */);
|
|
2264
2328
|
}
|
|
2265
2329
|
};
|
|
2330
|
+
this.sleepTime = 0;
|
|
2331
|
+
this.maxSleepTime = this._ref.storage.maxUploadRetryTime;
|
|
2266
2332
|
this._promise = new Promise((resolve, reject) => {
|
|
2267
2333
|
this._resolve = resolve;
|
|
2268
2334
|
this._reject = reject;
|
|
@@ -2272,6 +2338,9 @@ class UploadTask {
|
|
|
2272
2338
|
// to the top level with a dummy handler.
|
|
2273
2339
|
this._promise.then(null, () => { });
|
|
2274
2340
|
}
|
|
2341
|
+
isExponentialBackoffExpired() {
|
|
2342
|
+
return this.sleepTime > this.maxSleepTime;
|
|
2343
|
+
}
|
|
2275
2344
|
_makeProgressCallback() {
|
|
2276
2345
|
const sizeBefore = this._transferred;
|
|
2277
2346
|
return loaded => this._updateProgress(sizeBefore + loaded);
|
|
@@ -2301,7 +2370,9 @@ class UploadTask {
|
|
|
2301
2370
|
this._fetchMetadata();
|
|
2302
2371
|
}
|
|
2303
2372
|
else {
|
|
2304
|
-
|
|
2373
|
+
setTimeout(() => {
|
|
2374
|
+
this._continueUpload();
|
|
2375
|
+
}, this.sleepTime);
|
|
2305
2376
|
}
|
|
2306
2377
|
}
|
|
2307
2378
|
}
|
|
@@ -2377,7 +2448,9 @@ class UploadTask {
|
|
|
2377
2448
|
this._transition("error" /* ERROR */);
|
|
2378
2449
|
return;
|
|
2379
2450
|
}
|
|
2380
|
-
const uploadRequest = this._ref.storage._makeRequest(requestInfo, newTextConnection, authToken, appCheckToken
|
|
2451
|
+
const uploadRequest = this._ref.storage._makeRequest(requestInfo, newTextConnection, authToken, appCheckToken,
|
|
2452
|
+
/*retry=*/ false // Upload requests should not be retried as each retry should be preceded by another query request. Which is handled in this file.
|
|
2453
|
+
);
|
|
2381
2454
|
this._request = uploadRequest;
|
|
2382
2455
|
uploadRequest.getPromise().then((newStatus) => {
|
|
2383
2456
|
this._increaseMultiplier();
|
|
@@ -2396,7 +2469,7 @@ class UploadTask {
|
|
|
2396
2469
|
_increaseMultiplier() {
|
|
2397
2470
|
const currentSize = RESUMABLE_UPLOAD_CHUNK_SIZE * this._chunkMultiplier;
|
|
2398
2471
|
// Max chunk size is 32M.
|
|
2399
|
-
if (currentSize < 32 * 1024 * 1024) {
|
|
2472
|
+
if (currentSize * 2 < 32 * 1024 * 1024) {
|
|
2400
2473
|
this._chunkMultiplier *= 2;
|
|
2401
2474
|
}
|
|
2402
2475
|
}
|
|
@@ -2545,6 +2618,7 @@ class UploadTask {
|
|
|
2545
2618
|
* callbacks.
|
|
2546
2619
|
*/
|
|
2547
2620
|
on(type, nextOrObserver, error, completed) {
|
|
2621
|
+
// Note: `type` isn't being used. Its type is also incorrect. TaskEvent should not be a string.
|
|
2548
2622
|
const observer = new Observer(nextOrObserver || undefined, error || undefined, completed || undefined);
|
|
2549
2623
|
this._addObserver(observer);
|
|
2550
2624
|
return () => {
|
|
@@ -3247,9 +3321,9 @@ class FirebaseStorageImpl {
|
|
|
3247
3321
|
* @param requestInfo - HTTP RequestInfo object
|
|
3248
3322
|
* @param authToken - Firebase auth token
|
|
3249
3323
|
*/
|
|
3250
|
-
_makeRequest(requestInfo, requestFactory, authToken, appCheckToken) {
|
|
3324
|
+
_makeRequest(requestInfo, requestFactory, authToken, appCheckToken, retry = true) {
|
|
3251
3325
|
if (!this._deleted) {
|
|
3252
|
-
const request = makeRequest(requestInfo, this._appId, authToken, appCheckToken, requestFactory, this._firebaseVersion);
|
|
3326
|
+
const request = makeRequest(requestInfo, this._appId, authToken, appCheckToken, requestFactory, this._firebaseVersion, retry);
|
|
3253
3327
|
this._requests.add(request);
|
|
3254
3328
|
// Request removes itself from set when complete.
|
|
3255
3329
|
request.getPromise().then(() => this._requests.delete(request), () => this._requests.delete(request));
|
|
@@ -3269,7 +3343,7 @@ class FirebaseStorageImpl {
|
|
|
3269
3343
|
}
|
|
3270
3344
|
|
|
3271
3345
|
const name = "@firebase/storage";
|
|
3272
|
-
const version = "0.9.
|
|
3346
|
+
const version = "0.9.11-20221010203322";
|
|
3273
3347
|
|
|
3274
3348
|
/**
|
|
3275
3349
|
* @license
|
|
@@ -3484,11 +3558,9 @@ function getStorage(app = getApp(), bucketUrl) {
|
|
|
3484
3558
|
const storageInstance = storageProvider.getImmediate({
|
|
3485
3559
|
identifier: bucketUrl
|
|
3486
3560
|
});
|
|
3487
|
-
const
|
|
3488
|
-
if (
|
|
3489
|
-
|
|
3490
|
-
// eslint-disable-next-line no-restricted-globals
|
|
3491
|
-
connectStorageEmulator(storageInstance, host, parseInt(port, 10));
|
|
3561
|
+
const emulator = getDefaultEmulatorHostnameAndPort('storage');
|
|
3562
|
+
if (emulator) {
|
|
3563
|
+
connectStorageEmulator(storageInstance, ...emulator);
|
|
3492
3564
|
}
|
|
3493
3565
|
return storageInstance;
|
|
3494
3566
|
}
|