@uploadcare/upload-client 2.2.0 → 3.0.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 +15 -6
- package/dist/index.browser.cjs +74 -21
- package/dist/index.browser.js +75 -20
- package/dist/index.cjs +69 -21
- package/dist/index.js +70 -20
- package/dist/types.d.ts +10 -5
- package/package.json +3 -10
package/README.md
CHANGED
|
@@ -81,8 +81,8 @@ You can track uploading progress:
|
|
|
81
81
|
|
|
82
82
|
```javascript
|
|
83
83
|
const fileUUID = 'edfdf045-34c0-4087-bbdd-e3834921f890'
|
|
84
|
-
const onProgress = ({ value }) => {
|
|
85
|
-
console.log(value)
|
|
84
|
+
const onProgress = ({ isComputable, value }) => {
|
|
85
|
+
console.log(isComputable, value)
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
client
|
|
@@ -90,6 +90,10 @@ client
|
|
|
90
90
|
.then(file => console.log(file.uuid))
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
Note that `isComputable` flag can be `false` is some cases of uploading from the URL.
|
|
94
|
+
If we can't calculate the file size, progress info will look like `{ isComputable: false }` without a `value`.
|
|
95
|
+
Successful uploading progress will be always `{ isComputable: true, value: 1 }`.
|
|
96
|
+
|
|
93
97
|
You can cancel file uploading and track this event:
|
|
94
98
|
|
|
95
99
|
```javascript
|
|
@@ -183,7 +187,7 @@ Also, you can use low-level wrappers to call the API endpoints directly:
|
|
|
183
187
|
```javascript
|
|
184
188
|
import { base, AbortController } from '@uploadcare/upload-client'
|
|
185
189
|
|
|
186
|
-
const onProgress = ({ value }) => console.log(value)
|
|
190
|
+
const onProgress = ({ isComputable, value }) => console.log(isComputable, value)
|
|
187
191
|
const abortController = new AbortController()
|
|
188
192
|
|
|
189
193
|
base(fileData, { onProgress, signal: abortController }) // fileData must be `Blob` or `File` or `Buffer`
|
|
@@ -303,7 +307,7 @@ of `API secret key` and `secureExpire`.
|
|
|
303
307
|
|
|
304
308
|
Stands for the Unix time to which the signature is valid, e.g., `1454902434`.
|
|
305
309
|
|
|
306
|
-
#### `
|
|
310
|
+
#### `userAgent: string | CustomUserAgentFn`
|
|
307
311
|
|
|
308
312
|
```typescript
|
|
309
313
|
type CustomUserAgentOptions = {
|
|
@@ -321,6 +325,11 @@ type CustomUserAgentFn = (options: CustomUserAgentOptions) => string
|
|
|
321
325
|
|
|
322
326
|
Defaults to `UploadcareUploadClient/${version}/${publicKey} (JavaScript; ${integration})`
|
|
323
327
|
|
|
328
|
+
#### `integration: string`
|
|
329
|
+
|
|
330
|
+
Integration value passed to the `X-UC-User-Agent` header.
|
|
331
|
+
May be overrided with the custom user agent string or function.
|
|
332
|
+
|
|
324
333
|
#### `checkForUrlDuplicates: boolean`
|
|
325
334
|
|
|
326
335
|
Runs the duplicate check and provides the immediate-download behavior.
|
|
@@ -446,6 +455,6 @@ request at [hello@uploadcare.com][uc-email-hello].
|
|
|
446
455
|
[badge-release-url]: https://github.com/uploadcare/uploadcare-upload-client/releases
|
|
447
456
|
[npm-img]: http://img.shields.io/npm/v/@uploadcare/upload-client.svg
|
|
448
457
|
[npm-url]: https://www.npmjs.org/package/@uploadcare/upload-client
|
|
449
|
-
[badge-build]: https://
|
|
450
|
-
[build-url]: https://github.com/uploadcare/uploadcare-upload-client/actions
|
|
458
|
+
[badge-build]: https://github.com/uploadcare/uploadcare-upload-client/actions/workflows/checks.yml/badge.svg
|
|
459
|
+
[build-url]: https://github.com/uploadcare/uploadcare-upload-client/actions/workflows/checks.yml
|
|
451
460
|
[uc-docs-upload-api]: https://uploadcare.com/docs/api_reference/upload/?utm_source=github&utm_campaign=uploadcare-upload-client
|
package/dist/index.browser.cjs
CHANGED
|
@@ -152,7 +152,15 @@ var request = function (_a) {
|
|
|
152
152
|
};
|
|
153
153
|
if (onProgress && typeof onProgress === 'function') {
|
|
154
154
|
xhr.upload.onprogress = function (event) {
|
|
155
|
-
|
|
155
|
+
if (event.lengthComputable) {
|
|
156
|
+
onProgress({
|
|
157
|
+
isComputable: true,
|
|
158
|
+
value: event.loaded / event.total
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
onProgress({ isComputable: false });
|
|
163
|
+
}
|
|
156
164
|
};
|
|
157
165
|
}
|
|
158
166
|
if (data) {
|
|
@@ -242,7 +250,7 @@ var defaultSettings = {
|
|
|
242
250
|
var defaultContentType = 'application/octet-stream';
|
|
243
251
|
var defaultFilename = 'original';
|
|
244
252
|
|
|
245
|
-
var version = '
|
|
253
|
+
var version = '3.0.0';
|
|
246
254
|
|
|
247
255
|
/**
|
|
248
256
|
* Returns User Agent based on version and settings.
|
|
@@ -627,13 +635,17 @@ function multipartUpload(part, url, _a) {
|
|
|
627
635
|
method: 'PUT',
|
|
628
636
|
url: url,
|
|
629
637
|
data: part,
|
|
638
|
+
// Upload request can't be non-computable because we always know exact size
|
|
630
639
|
onProgress: onProgress,
|
|
631
640
|
signal: signal
|
|
632
641
|
})
|
|
633
642
|
.then(function (result) {
|
|
634
643
|
// hack for node ¯\_(ツ)_/¯
|
|
635
644
|
if (onProgress)
|
|
636
|
-
onProgress({
|
|
645
|
+
onProgress({
|
|
646
|
+
isComputable: true,
|
|
647
|
+
value: 1
|
|
648
|
+
});
|
|
637
649
|
return result;
|
|
638
650
|
})
|
|
639
651
|
.then(function (_a) {
|
|
@@ -756,7 +768,7 @@ function isReadyPoll(_a) {
|
|
|
756
768
|
if (response.isReady) {
|
|
757
769
|
return response;
|
|
758
770
|
}
|
|
759
|
-
onProgress && onProgress({ value: 1 });
|
|
771
|
+
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
760
772
|
return false;
|
|
761
773
|
});
|
|
762
774
|
},
|
|
@@ -1018,13 +1030,25 @@ function pollStrategy(_a) {
|
|
|
1018
1030
|
return new UploadClientError("Token \"" + token + "\" was not found.");
|
|
1019
1031
|
}
|
|
1020
1032
|
case Status.Progress: {
|
|
1021
|
-
if (onProgress)
|
|
1022
|
-
|
|
1033
|
+
if (onProgress) {
|
|
1034
|
+
if (response.total === 'unknown') {
|
|
1035
|
+
onProgress({ isComputable: false });
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
onProgress({
|
|
1039
|
+
isComputable: true,
|
|
1040
|
+
value: response.done / response.total
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1023
1044
|
return false;
|
|
1024
1045
|
}
|
|
1025
1046
|
case Status.Success: {
|
|
1026
1047
|
if (onProgress)
|
|
1027
|
-
onProgress({
|
|
1048
|
+
onProgress({
|
|
1049
|
+
isComputable: true,
|
|
1050
|
+
value: response.done / response.total
|
|
1051
|
+
});
|
|
1028
1052
|
return response;
|
|
1029
1053
|
}
|
|
1030
1054
|
default: {
|
|
@@ -1053,14 +1077,25 @@ var pushStrategy = function (_a) {
|
|
|
1053
1077
|
switch (result.status) {
|
|
1054
1078
|
case Status.Progress: {
|
|
1055
1079
|
if (onProgress) {
|
|
1056
|
-
|
|
1080
|
+
if (result.total === 'unknown') {
|
|
1081
|
+
onProgress({ isComputable: false });
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
onProgress({
|
|
1085
|
+
isComputable: true,
|
|
1086
|
+
value: result.done / result.total
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1057
1089
|
}
|
|
1058
1090
|
break;
|
|
1059
1091
|
}
|
|
1060
1092
|
case Status.Success: {
|
|
1061
1093
|
destroy();
|
|
1062
1094
|
if (onProgress)
|
|
1063
|
-
onProgress({
|
|
1095
|
+
onProgress({
|
|
1096
|
+
isComputable: true,
|
|
1097
|
+
value: result.done / result.total
|
|
1098
|
+
});
|
|
1064
1099
|
resolve(result);
|
|
1065
1100
|
break;
|
|
1066
1101
|
}
|
|
@@ -1163,7 +1198,10 @@ var uploadFromUploaded = function (uuid, _a) {
|
|
|
1163
1198
|
.then(function (result) {
|
|
1164
1199
|
// hack for node ¯\_(ツ)_/¯
|
|
1165
1200
|
if (onProgress)
|
|
1166
|
-
onProgress({
|
|
1201
|
+
onProgress({
|
|
1202
|
+
isComputable: true,
|
|
1203
|
+
value: 1
|
|
1204
|
+
});
|
|
1167
1205
|
return result;
|
|
1168
1206
|
});
|
|
1169
1207
|
};
|
|
@@ -1278,19 +1316,24 @@ var uploadMultipart = function (file, _a) {
|
|
|
1278
1316
|
var publicKey = _a.publicKey, fileName = _a.fileName, fileSize = _a.fileSize, baseURL = _a.baseURL, secureSignature = _a.secureSignature, secureExpire = _a.secureExpire, store = _a.store, signal = _a.signal, onProgress = _a.onProgress, source = _a.source, integration = _a.integration, userAgent = _a.userAgent, retryThrottledRequestMaxTimes = _a.retryThrottledRequestMaxTimes, contentType = _a.contentType, _b = _a.multipartChunkSize, multipartChunkSize = _b === void 0 ? defaultSettings.multipartChunkSize : _b, _c = _a.maxConcurrentRequests, maxConcurrentRequests = _c === void 0 ? defaultSettings.maxConcurrentRequests : _c, _d = _a.multipartMaxAttempts, multipartMaxAttempts = _d === void 0 ? defaultSettings.multipartMaxAttempts : _d, baseCDN = _a.baseCDN;
|
|
1279
1317
|
var size = fileSize || getFileSize(file);
|
|
1280
1318
|
var progressValues;
|
|
1281
|
-
var createProgressHandler = function (
|
|
1319
|
+
var createProgressHandler = function (totalChunks, chunkIdx) {
|
|
1282
1320
|
if (!onProgress)
|
|
1283
1321
|
return;
|
|
1284
1322
|
if (!progressValues) {
|
|
1285
|
-
progressValues = Array(
|
|
1323
|
+
progressValues = Array(totalChunks).fill(0);
|
|
1286
1324
|
}
|
|
1287
1325
|
var sum = function (values) {
|
|
1288
1326
|
return values.reduce(function (sum, next) { return sum + next; }, 0);
|
|
1289
1327
|
};
|
|
1290
|
-
return function (
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1328
|
+
return function (info) {
|
|
1329
|
+
if (!info.isComputable) {
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
progressValues[chunkIdx] = info.value;
|
|
1333
|
+
onProgress({
|
|
1334
|
+
isComputable: true,
|
|
1335
|
+
value: sum(progressValues) / totalChunks
|
|
1336
|
+
});
|
|
1294
1337
|
};
|
|
1295
1338
|
};
|
|
1296
1339
|
return multipartStart(size, {
|
|
@@ -1495,6 +1538,7 @@ function uploadFileGroup(data, _a) {
|
|
|
1495
1538
|
throw new TypeError("Group uploading from \"" + data + "\" is not supported");
|
|
1496
1539
|
}
|
|
1497
1540
|
var progressValues;
|
|
1541
|
+
var isStillComputable = true;
|
|
1498
1542
|
var filesCount = data.length;
|
|
1499
1543
|
var createProgressHandler = function (size, index) {
|
|
1500
1544
|
if (!onProgress)
|
|
@@ -1505,10 +1549,14 @@ function uploadFileGroup(data, _a) {
|
|
|
1505
1549
|
var normalize = function (values) {
|
|
1506
1550
|
return values.reduce(function (sum, next) { return sum + next; }) / size;
|
|
1507
1551
|
};
|
|
1508
|
-
return function (
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1552
|
+
return function (info) {
|
|
1553
|
+
if (!info.isComputable || !isStillComputable) {
|
|
1554
|
+
isStillComputable = false;
|
|
1555
|
+
onProgress({ isComputable: false });
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
progressValues[index] = info.value;
|
|
1559
|
+
onProgress({ isComputable: true, value: normalize(progressValues) });
|
|
1512
1560
|
};
|
|
1513
1561
|
};
|
|
1514
1562
|
return Promise.all(data.map(function (file, index) {
|
|
@@ -1549,7 +1597,12 @@ function uploadFileGroup(data, _a) {
|
|
|
1549
1597
|
integration: integration,
|
|
1550
1598
|
userAgent: userAgent,
|
|
1551
1599
|
retryThrottledRequestMaxTimes: retryThrottledRequestMaxTimes
|
|
1552
|
-
})
|
|
1600
|
+
})
|
|
1601
|
+
.then(function (groupInfo) { return new UploadcareGroup(groupInfo, filesInGroup); })
|
|
1602
|
+
.then(function (group) {
|
|
1603
|
+
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
1604
|
+
return group;
|
|
1605
|
+
});
|
|
1553
1606
|
});
|
|
1554
1607
|
}
|
|
1555
1608
|
|
package/dist/index.browser.js
CHANGED
|
@@ -94,7 +94,15 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
|
|
|
94
94
|
};
|
|
95
95
|
if (onProgress && typeof onProgress === 'function') {
|
|
96
96
|
xhr.upload.onprogress = (event) => {
|
|
97
|
-
|
|
97
|
+
if (event.lengthComputable) {
|
|
98
|
+
onProgress({
|
|
99
|
+
isComputable: true,
|
|
100
|
+
value: event.loaded / event.total
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
onProgress({ isComputable: false });
|
|
105
|
+
}
|
|
98
106
|
};
|
|
99
107
|
}
|
|
100
108
|
if (data) {
|
|
@@ -168,7 +176,7 @@ const defaultSettings = {
|
|
|
168
176
|
const defaultContentType = 'application/octet-stream';
|
|
169
177
|
const defaultFilename = 'original';
|
|
170
178
|
|
|
171
|
-
var version = '
|
|
179
|
+
var version = '3.0.0';
|
|
172
180
|
|
|
173
181
|
/**
|
|
174
182
|
* Returns User Agent based on version and settings.
|
|
@@ -515,13 +523,17 @@ function multipartUpload(part, url, { signal, onProgress }) {
|
|
|
515
523
|
method: 'PUT',
|
|
516
524
|
url,
|
|
517
525
|
data: part,
|
|
518
|
-
|
|
526
|
+
// Upload request can't be non-computable because we always know exact size
|
|
527
|
+
onProgress: onProgress,
|
|
519
528
|
signal
|
|
520
529
|
})
|
|
521
530
|
.then((result) => {
|
|
522
531
|
// hack for node ¯\_(ツ)_/¯
|
|
523
532
|
if (onProgress)
|
|
524
|
-
onProgress({
|
|
533
|
+
onProgress({
|
|
534
|
+
isComputable: true,
|
|
535
|
+
value: 1
|
|
536
|
+
});
|
|
525
537
|
return result;
|
|
526
538
|
})
|
|
527
539
|
.then(({ status }) => ({ code: status }));
|
|
@@ -630,7 +642,7 @@ function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent,
|
|
|
630
642
|
if (response.isReady) {
|
|
631
643
|
return response;
|
|
632
644
|
}
|
|
633
|
-
onProgress && onProgress({ value: 1 });
|
|
645
|
+
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
634
646
|
return false;
|
|
635
647
|
}),
|
|
636
648
|
signal
|
|
@@ -880,13 +892,25 @@ function pollStrategy({ token, publicKey, baseURL, integration, userAgent, retry
|
|
|
880
892
|
return new UploadClientError(`Token "${token}" was not found.`);
|
|
881
893
|
}
|
|
882
894
|
case Status.Progress: {
|
|
883
|
-
if (onProgress)
|
|
884
|
-
|
|
895
|
+
if (onProgress) {
|
|
896
|
+
if (response.total === 'unknown') {
|
|
897
|
+
onProgress({ isComputable: false });
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
onProgress({
|
|
901
|
+
isComputable: true,
|
|
902
|
+
value: response.done / response.total
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
}
|
|
885
906
|
return false;
|
|
886
907
|
}
|
|
887
908
|
case Status.Success: {
|
|
888
909
|
if (onProgress)
|
|
889
|
-
onProgress({
|
|
910
|
+
onProgress({
|
|
911
|
+
isComputable: true,
|
|
912
|
+
value: response.done / response.total
|
|
913
|
+
});
|
|
890
914
|
return response;
|
|
891
915
|
}
|
|
892
916
|
default: {
|
|
@@ -912,14 +936,25 @@ const pushStrategy = ({ token, pusherKey, signal, onProgress }) => new Promise((
|
|
|
912
936
|
switch (result.status) {
|
|
913
937
|
case Status.Progress: {
|
|
914
938
|
if (onProgress) {
|
|
915
|
-
|
|
939
|
+
if (result.total === 'unknown') {
|
|
940
|
+
onProgress({ isComputable: false });
|
|
941
|
+
}
|
|
942
|
+
else {
|
|
943
|
+
onProgress({
|
|
944
|
+
isComputable: true,
|
|
945
|
+
value: result.done / result.total
|
|
946
|
+
});
|
|
947
|
+
}
|
|
916
948
|
}
|
|
917
949
|
break;
|
|
918
950
|
}
|
|
919
951
|
case Status.Success: {
|
|
920
952
|
destroy();
|
|
921
953
|
if (onProgress)
|
|
922
|
-
onProgress({
|
|
954
|
+
onProgress({
|
|
955
|
+
isComputable: true,
|
|
956
|
+
value: result.done / result.total
|
|
957
|
+
});
|
|
923
958
|
resolve(result);
|
|
924
959
|
break;
|
|
925
960
|
}
|
|
@@ -1007,7 +1042,10 @@ const uploadFromUploaded = (uuid, { publicKey, fileName, baseURL, signal, onProg
|
|
|
1007
1042
|
.then((result) => {
|
|
1008
1043
|
// hack for node ¯\_(ツ)_/¯
|
|
1009
1044
|
if (onProgress)
|
|
1010
|
-
onProgress({
|
|
1045
|
+
onProgress({
|
|
1046
|
+
isComputable: true,
|
|
1047
|
+
value: 1
|
|
1048
|
+
});
|
|
1011
1049
|
return result;
|
|
1012
1050
|
});
|
|
1013
1051
|
};
|
|
@@ -1112,16 +1150,22 @@ const uploadPartWithRetry = (chunk, url, { publicKey, onProgress, signal, integr
|
|
|
1112
1150
|
const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, maxConcurrentRequests = defaultSettings.maxConcurrentRequests, multipartMaxAttempts = defaultSettings.multipartMaxAttempts, baseCDN }) => {
|
|
1113
1151
|
const size = fileSize || getFileSize(file);
|
|
1114
1152
|
let progressValues;
|
|
1115
|
-
const createProgressHandler = (
|
|
1153
|
+
const createProgressHandler = (totalChunks, chunkIdx) => {
|
|
1116
1154
|
if (!onProgress)
|
|
1117
1155
|
return;
|
|
1118
1156
|
if (!progressValues) {
|
|
1119
|
-
progressValues = Array(
|
|
1157
|
+
progressValues = Array(totalChunks).fill(0);
|
|
1120
1158
|
}
|
|
1121
1159
|
const sum = (values) => values.reduce((sum, next) => sum + next, 0);
|
|
1122
|
-
return (
|
|
1123
|
-
|
|
1124
|
-
|
|
1160
|
+
return (info) => {
|
|
1161
|
+
if (!info.isComputable) {
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
progressValues[chunkIdx] = info.value;
|
|
1165
|
+
onProgress({
|
|
1166
|
+
isComputable: true,
|
|
1167
|
+
value: sum(progressValues) / totalChunks
|
|
1168
|
+
});
|
|
1125
1169
|
};
|
|
1126
1170
|
};
|
|
1127
1171
|
return multipartStart(size, {
|
|
@@ -1314,6 +1358,7 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
|
|
|
1314
1358
|
throw new TypeError(`Group uploading from "${data}" is not supported`);
|
|
1315
1359
|
}
|
|
1316
1360
|
let progressValues;
|
|
1361
|
+
let isStillComputable = true;
|
|
1317
1362
|
const filesCount = data.length;
|
|
1318
1363
|
const createProgressHandler = (size, index) => {
|
|
1319
1364
|
if (!onProgress)
|
|
@@ -1322,9 +1367,14 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
|
|
|
1322
1367
|
progressValues = Array(size).fill(0);
|
|
1323
1368
|
}
|
|
1324
1369
|
const normalize = (values) => values.reduce((sum, next) => sum + next) / size;
|
|
1325
|
-
return (
|
|
1326
|
-
|
|
1327
|
-
|
|
1370
|
+
return (info) => {
|
|
1371
|
+
if (!info.isComputable || !isStillComputable) {
|
|
1372
|
+
isStillComputable = false;
|
|
1373
|
+
onProgress({ isComputable: false });
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
progressValues[index] = info.value;
|
|
1377
|
+
onProgress({ isComputable: true, value: normalize(progressValues) });
|
|
1328
1378
|
};
|
|
1329
1379
|
};
|
|
1330
1380
|
return Promise.all(data.map((file, index) => uploadFile(file, {
|
|
@@ -1363,7 +1413,12 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
|
|
|
1363
1413
|
integration,
|
|
1364
1414
|
userAgent,
|
|
1365
1415
|
retryThrottledRequestMaxTimes
|
|
1366
|
-
})
|
|
1416
|
+
})
|
|
1417
|
+
.then((groupInfo) => new UploadcareGroup(groupInfo, filesInGroup))
|
|
1418
|
+
.then((group) => {
|
|
1419
|
+
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
1420
|
+
return group;
|
|
1421
|
+
});
|
|
1367
1422
|
});
|
|
1368
1423
|
}
|
|
1369
1424
|
|
package/dist/index.cjs
CHANGED
|
@@ -112,7 +112,10 @@ var ProgressEmitter = /** @class */ (function (_super) {
|
|
|
112
112
|
}
|
|
113
113
|
ProgressEmitter.prototype._transform = function (chunk, encoding, callback) {
|
|
114
114
|
this._position += chunk.length;
|
|
115
|
-
this._onprogress({
|
|
115
|
+
this._onprogress({
|
|
116
|
+
isComputable: true,
|
|
117
|
+
value: this._position / this.size
|
|
118
|
+
});
|
|
116
119
|
callback(null, chunk);
|
|
117
120
|
};
|
|
118
121
|
return ProgressEmitter;
|
|
@@ -286,7 +289,7 @@ var defaultSettings = {
|
|
|
286
289
|
var defaultContentType = 'application/octet-stream';
|
|
287
290
|
var defaultFilename = 'original';
|
|
288
291
|
|
|
289
|
-
var version = '
|
|
292
|
+
var version = '3.0.0';
|
|
290
293
|
|
|
291
294
|
/**
|
|
292
295
|
* Returns User Agent based on version and settings.
|
|
@@ -671,13 +674,17 @@ function multipartUpload(part, url, _a) {
|
|
|
671
674
|
method: 'PUT',
|
|
672
675
|
url: url,
|
|
673
676
|
data: part,
|
|
677
|
+
// Upload request can't be non-computable because we always know exact size
|
|
674
678
|
onProgress: onProgress,
|
|
675
679
|
signal: signal
|
|
676
680
|
})
|
|
677
681
|
.then(function (result) {
|
|
678
682
|
// hack for node ¯\_(ツ)_/¯
|
|
679
683
|
if (onProgress)
|
|
680
|
-
onProgress({
|
|
684
|
+
onProgress({
|
|
685
|
+
isComputable: true,
|
|
686
|
+
value: 1
|
|
687
|
+
});
|
|
681
688
|
return result;
|
|
682
689
|
})
|
|
683
690
|
.then(function (_a) {
|
|
@@ -800,7 +807,7 @@ function isReadyPoll(_a) {
|
|
|
800
807
|
if (response.isReady) {
|
|
801
808
|
return response;
|
|
802
809
|
}
|
|
803
|
-
onProgress && onProgress({ value: 1 });
|
|
810
|
+
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
804
811
|
return false;
|
|
805
812
|
});
|
|
806
813
|
},
|
|
@@ -1052,13 +1059,25 @@ function pollStrategy(_a) {
|
|
|
1052
1059
|
return new UploadClientError("Token \"" + token + "\" was not found.");
|
|
1053
1060
|
}
|
|
1054
1061
|
case Status.Progress: {
|
|
1055
|
-
if (onProgress)
|
|
1056
|
-
|
|
1062
|
+
if (onProgress) {
|
|
1063
|
+
if (response.total === 'unknown') {
|
|
1064
|
+
onProgress({ isComputable: false });
|
|
1065
|
+
}
|
|
1066
|
+
else {
|
|
1067
|
+
onProgress({
|
|
1068
|
+
isComputable: true,
|
|
1069
|
+
value: response.done / response.total
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1057
1073
|
return false;
|
|
1058
1074
|
}
|
|
1059
1075
|
case Status.Success: {
|
|
1060
1076
|
if (onProgress)
|
|
1061
|
-
onProgress({
|
|
1077
|
+
onProgress({
|
|
1078
|
+
isComputable: true,
|
|
1079
|
+
value: response.done / response.total
|
|
1080
|
+
});
|
|
1062
1081
|
return response;
|
|
1063
1082
|
}
|
|
1064
1083
|
default: {
|
|
@@ -1087,14 +1106,25 @@ var pushStrategy = function (_a) {
|
|
|
1087
1106
|
switch (result.status) {
|
|
1088
1107
|
case Status.Progress: {
|
|
1089
1108
|
if (onProgress) {
|
|
1090
|
-
|
|
1109
|
+
if (result.total === 'unknown') {
|
|
1110
|
+
onProgress({ isComputable: false });
|
|
1111
|
+
}
|
|
1112
|
+
else {
|
|
1113
|
+
onProgress({
|
|
1114
|
+
isComputable: true,
|
|
1115
|
+
value: result.done / result.total
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1091
1118
|
}
|
|
1092
1119
|
break;
|
|
1093
1120
|
}
|
|
1094
1121
|
case Status.Success: {
|
|
1095
1122
|
destroy();
|
|
1096
1123
|
if (onProgress)
|
|
1097
|
-
onProgress({
|
|
1124
|
+
onProgress({
|
|
1125
|
+
isComputable: true,
|
|
1126
|
+
value: result.done / result.total
|
|
1127
|
+
});
|
|
1098
1128
|
resolve(result);
|
|
1099
1129
|
break;
|
|
1100
1130
|
}
|
|
@@ -1197,7 +1227,10 @@ var uploadFromUploaded = function (uuid, _a) {
|
|
|
1197
1227
|
.then(function (result) {
|
|
1198
1228
|
// hack for node ¯\_(ツ)_/¯
|
|
1199
1229
|
if (onProgress)
|
|
1200
|
-
onProgress({
|
|
1230
|
+
onProgress({
|
|
1231
|
+
isComputable: true,
|
|
1232
|
+
value: 1
|
|
1233
|
+
});
|
|
1201
1234
|
return result;
|
|
1202
1235
|
});
|
|
1203
1236
|
};
|
|
@@ -1312,19 +1345,24 @@ var uploadMultipart = function (file, _a) {
|
|
|
1312
1345
|
var publicKey = _a.publicKey, fileName = _a.fileName, fileSize = _a.fileSize, baseURL = _a.baseURL, secureSignature = _a.secureSignature, secureExpire = _a.secureExpire, store = _a.store, signal = _a.signal, onProgress = _a.onProgress, source = _a.source, integration = _a.integration, userAgent = _a.userAgent, retryThrottledRequestMaxTimes = _a.retryThrottledRequestMaxTimes, contentType = _a.contentType, _b = _a.multipartChunkSize, multipartChunkSize = _b === void 0 ? defaultSettings.multipartChunkSize : _b, _c = _a.maxConcurrentRequests, maxConcurrentRequests = _c === void 0 ? defaultSettings.maxConcurrentRequests : _c, _d = _a.multipartMaxAttempts, multipartMaxAttempts = _d === void 0 ? defaultSettings.multipartMaxAttempts : _d, baseCDN = _a.baseCDN;
|
|
1313
1346
|
var size = fileSize || getFileSize(file);
|
|
1314
1347
|
var progressValues;
|
|
1315
|
-
var createProgressHandler = function (
|
|
1348
|
+
var createProgressHandler = function (totalChunks, chunkIdx) {
|
|
1316
1349
|
if (!onProgress)
|
|
1317
1350
|
return;
|
|
1318
1351
|
if (!progressValues) {
|
|
1319
|
-
progressValues = Array(
|
|
1352
|
+
progressValues = Array(totalChunks).fill(0);
|
|
1320
1353
|
}
|
|
1321
1354
|
var sum = function (values) {
|
|
1322
1355
|
return values.reduce(function (sum, next) { return sum + next; }, 0);
|
|
1323
1356
|
};
|
|
1324
|
-
return function (
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1357
|
+
return function (info) {
|
|
1358
|
+
if (!info.isComputable) {
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
progressValues[chunkIdx] = info.value;
|
|
1362
|
+
onProgress({
|
|
1363
|
+
isComputable: true,
|
|
1364
|
+
value: sum(progressValues) / totalChunks
|
|
1365
|
+
});
|
|
1328
1366
|
};
|
|
1329
1367
|
};
|
|
1330
1368
|
return multipartStart(size, {
|
|
@@ -1529,6 +1567,7 @@ function uploadFileGroup(data, _a) {
|
|
|
1529
1567
|
throw new TypeError("Group uploading from \"" + data + "\" is not supported");
|
|
1530
1568
|
}
|
|
1531
1569
|
var progressValues;
|
|
1570
|
+
var isStillComputable = true;
|
|
1532
1571
|
var filesCount = data.length;
|
|
1533
1572
|
var createProgressHandler = function (size, index) {
|
|
1534
1573
|
if (!onProgress)
|
|
@@ -1539,10 +1578,14 @@ function uploadFileGroup(data, _a) {
|
|
|
1539
1578
|
var normalize = function (values) {
|
|
1540
1579
|
return values.reduce(function (sum, next) { return sum + next; }) / size;
|
|
1541
1580
|
};
|
|
1542
|
-
return function (
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1581
|
+
return function (info) {
|
|
1582
|
+
if (!info.isComputable || !isStillComputable) {
|
|
1583
|
+
isStillComputable = false;
|
|
1584
|
+
onProgress({ isComputable: false });
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
progressValues[index] = info.value;
|
|
1588
|
+
onProgress({ isComputable: true, value: normalize(progressValues) });
|
|
1546
1589
|
};
|
|
1547
1590
|
};
|
|
1548
1591
|
return Promise.all(data.map(function (file, index) {
|
|
@@ -1583,7 +1626,12 @@ function uploadFileGroup(data, _a) {
|
|
|
1583
1626
|
integration: integration,
|
|
1584
1627
|
userAgent: userAgent,
|
|
1585
1628
|
retryThrottledRequestMaxTimes: retryThrottledRequestMaxTimes
|
|
1586
|
-
})
|
|
1629
|
+
})
|
|
1630
|
+
.then(function (groupInfo) { return new UploadcareGroup(groupInfo, filesInGroup); })
|
|
1631
|
+
.then(function (group) {
|
|
1632
|
+
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
1633
|
+
return group;
|
|
1634
|
+
});
|
|
1587
1635
|
});
|
|
1588
1636
|
}
|
|
1589
1637
|
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,10 @@ class ProgressEmitter extends Transform {
|
|
|
48
48
|
}
|
|
49
49
|
_transform(chunk, encoding, callback) {
|
|
50
50
|
this._position += chunk.length;
|
|
51
|
-
this._onprogress({
|
|
51
|
+
this._onprogress({
|
|
52
|
+
isComputable: true,
|
|
53
|
+
value: this._position / this.size
|
|
54
|
+
});
|
|
52
55
|
callback(null, chunk);
|
|
53
56
|
}
|
|
54
57
|
}
|
|
@@ -200,7 +203,7 @@ const defaultSettings = {
|
|
|
200
203
|
const defaultContentType = 'application/octet-stream';
|
|
201
204
|
const defaultFilename = 'original';
|
|
202
205
|
|
|
203
|
-
var version = '
|
|
206
|
+
var version = '3.0.0';
|
|
204
207
|
|
|
205
208
|
/**
|
|
206
209
|
* Returns User Agent based on version and settings.
|
|
@@ -547,13 +550,17 @@ function multipartUpload(part, url, { signal, onProgress }) {
|
|
|
547
550
|
method: 'PUT',
|
|
548
551
|
url,
|
|
549
552
|
data: part,
|
|
550
|
-
|
|
553
|
+
// Upload request can't be non-computable because we always know exact size
|
|
554
|
+
onProgress: onProgress,
|
|
551
555
|
signal
|
|
552
556
|
})
|
|
553
557
|
.then((result) => {
|
|
554
558
|
// hack for node ¯\_(ツ)_/¯
|
|
555
559
|
if (onProgress)
|
|
556
|
-
onProgress({
|
|
560
|
+
onProgress({
|
|
561
|
+
isComputable: true,
|
|
562
|
+
value: 1
|
|
563
|
+
});
|
|
557
564
|
return result;
|
|
558
565
|
})
|
|
559
566
|
.then(({ status }) => ({ code: status }));
|
|
@@ -662,7 +669,7 @@ function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent,
|
|
|
662
669
|
if (response.isReady) {
|
|
663
670
|
return response;
|
|
664
671
|
}
|
|
665
|
-
onProgress && onProgress({ value: 1 });
|
|
672
|
+
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
666
673
|
return false;
|
|
667
674
|
}),
|
|
668
675
|
signal
|
|
@@ -902,13 +909,25 @@ function pollStrategy({ token, publicKey, baseURL, integration, userAgent, retry
|
|
|
902
909
|
return new UploadClientError(`Token "${token}" was not found.`);
|
|
903
910
|
}
|
|
904
911
|
case Status.Progress: {
|
|
905
|
-
if (onProgress)
|
|
906
|
-
|
|
912
|
+
if (onProgress) {
|
|
913
|
+
if (response.total === 'unknown') {
|
|
914
|
+
onProgress({ isComputable: false });
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
onProgress({
|
|
918
|
+
isComputable: true,
|
|
919
|
+
value: response.done / response.total
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
}
|
|
907
923
|
return false;
|
|
908
924
|
}
|
|
909
925
|
case Status.Success: {
|
|
910
926
|
if (onProgress)
|
|
911
|
-
onProgress({
|
|
927
|
+
onProgress({
|
|
928
|
+
isComputable: true,
|
|
929
|
+
value: response.done / response.total
|
|
930
|
+
});
|
|
912
931
|
return response;
|
|
913
932
|
}
|
|
914
933
|
default: {
|
|
@@ -934,14 +953,25 @@ const pushStrategy = ({ token, pusherKey, signal, onProgress }) => new Promise((
|
|
|
934
953
|
switch (result.status) {
|
|
935
954
|
case Status.Progress: {
|
|
936
955
|
if (onProgress) {
|
|
937
|
-
|
|
956
|
+
if (result.total === 'unknown') {
|
|
957
|
+
onProgress({ isComputable: false });
|
|
958
|
+
}
|
|
959
|
+
else {
|
|
960
|
+
onProgress({
|
|
961
|
+
isComputable: true,
|
|
962
|
+
value: result.done / result.total
|
|
963
|
+
});
|
|
964
|
+
}
|
|
938
965
|
}
|
|
939
966
|
break;
|
|
940
967
|
}
|
|
941
968
|
case Status.Success: {
|
|
942
969
|
destroy();
|
|
943
970
|
if (onProgress)
|
|
944
|
-
onProgress({
|
|
971
|
+
onProgress({
|
|
972
|
+
isComputable: true,
|
|
973
|
+
value: result.done / result.total
|
|
974
|
+
});
|
|
945
975
|
resolve(result);
|
|
946
976
|
break;
|
|
947
977
|
}
|
|
@@ -1029,7 +1059,10 @@ const uploadFromUploaded = (uuid, { publicKey, fileName, baseURL, signal, onProg
|
|
|
1029
1059
|
.then((result) => {
|
|
1030
1060
|
// hack for node ¯\_(ツ)_/¯
|
|
1031
1061
|
if (onProgress)
|
|
1032
|
-
onProgress({
|
|
1062
|
+
onProgress({
|
|
1063
|
+
isComputable: true,
|
|
1064
|
+
value: 1
|
|
1065
|
+
});
|
|
1033
1066
|
return result;
|
|
1034
1067
|
});
|
|
1035
1068
|
};
|
|
@@ -1134,16 +1167,22 @@ const uploadPartWithRetry = (chunk, url, { publicKey, onProgress, signal, integr
|
|
|
1134
1167
|
const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, maxConcurrentRequests = defaultSettings.maxConcurrentRequests, multipartMaxAttempts = defaultSettings.multipartMaxAttempts, baseCDN }) => {
|
|
1135
1168
|
const size = fileSize || getFileSize(file);
|
|
1136
1169
|
let progressValues;
|
|
1137
|
-
const createProgressHandler = (
|
|
1170
|
+
const createProgressHandler = (totalChunks, chunkIdx) => {
|
|
1138
1171
|
if (!onProgress)
|
|
1139
1172
|
return;
|
|
1140
1173
|
if (!progressValues) {
|
|
1141
|
-
progressValues = Array(
|
|
1174
|
+
progressValues = Array(totalChunks).fill(0);
|
|
1142
1175
|
}
|
|
1143
1176
|
const sum = (values) => values.reduce((sum, next) => sum + next, 0);
|
|
1144
|
-
return (
|
|
1145
|
-
|
|
1146
|
-
|
|
1177
|
+
return (info) => {
|
|
1178
|
+
if (!info.isComputable) {
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
progressValues[chunkIdx] = info.value;
|
|
1182
|
+
onProgress({
|
|
1183
|
+
isComputable: true,
|
|
1184
|
+
value: sum(progressValues) / totalChunks
|
|
1185
|
+
});
|
|
1147
1186
|
};
|
|
1148
1187
|
};
|
|
1149
1188
|
return multipartStart(size, {
|
|
@@ -1336,6 +1375,7 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
|
|
|
1336
1375
|
throw new TypeError(`Group uploading from "${data}" is not supported`);
|
|
1337
1376
|
}
|
|
1338
1377
|
let progressValues;
|
|
1378
|
+
let isStillComputable = true;
|
|
1339
1379
|
const filesCount = data.length;
|
|
1340
1380
|
const createProgressHandler = (size, index) => {
|
|
1341
1381
|
if (!onProgress)
|
|
@@ -1344,9 +1384,14 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
|
|
|
1344
1384
|
progressValues = Array(size).fill(0);
|
|
1345
1385
|
}
|
|
1346
1386
|
const normalize = (values) => values.reduce((sum, next) => sum + next) / size;
|
|
1347
|
-
return (
|
|
1348
|
-
|
|
1349
|
-
|
|
1387
|
+
return (info) => {
|
|
1388
|
+
if (!info.isComputable || !isStillComputable) {
|
|
1389
|
+
isStillComputable = false;
|
|
1390
|
+
onProgress({ isComputable: false });
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
progressValues[index] = info.value;
|
|
1394
|
+
onProgress({ isComputable: true, value: normalize(progressValues) });
|
|
1350
1395
|
};
|
|
1351
1396
|
};
|
|
1352
1397
|
return Promise.all(data.map((file, index) => uploadFile(file, {
|
|
@@ -1385,7 +1430,12 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
|
|
|
1385
1430
|
integration,
|
|
1386
1431
|
userAgent,
|
|
1387
1432
|
retryThrottledRequestMaxTimes
|
|
1388
|
-
})
|
|
1433
|
+
})
|
|
1434
|
+
.then((groupInfo) => new UploadcareGroup(groupInfo, filesInGroup))
|
|
1435
|
+
.then((group) => {
|
|
1436
|
+
onProgress && onProgress({ isComputable: true, value: 1 });
|
|
1437
|
+
return group;
|
|
1438
|
+
});
|
|
1389
1439
|
});
|
|
1390
1440
|
}
|
|
1391
1441
|
|
package/dist/types.d.ts
CHANGED
|
@@ -70,9 +70,14 @@ export declare type Token = string;
|
|
|
70
70
|
export declare type Uuid = string;
|
|
71
71
|
export declare type GroupId = string;
|
|
72
72
|
export declare type Url = string;
|
|
73
|
-
export declare type
|
|
73
|
+
export declare type ComputableProgressInfo = {
|
|
74
|
+
isComputable: true;
|
|
74
75
|
value: number;
|
|
75
|
-
}
|
|
76
|
+
};
|
|
77
|
+
export declare type UnknownProgressInfo = {
|
|
78
|
+
isComputable: false;
|
|
79
|
+
};
|
|
80
|
+
export declare type ProgressCallback<T = ComputableProgressInfo | UnknownProgressInfo> = (arg: T) => void;
|
|
76
81
|
export interface DefaultSettings {
|
|
77
82
|
baseCDN: string;
|
|
78
83
|
baseURL: string;
|
|
@@ -185,7 +190,7 @@ export declare type StatusProgressResponse = {
|
|
|
185
190
|
status: Status.Progress;
|
|
186
191
|
size: number;
|
|
187
192
|
done: number;
|
|
188
|
-
total: number;
|
|
193
|
+
total: number | "unknown";
|
|
189
194
|
};
|
|
190
195
|
export declare type StatusErrorResponse = {
|
|
191
196
|
status: Status.Error;
|
|
@@ -262,7 +267,7 @@ declare function multipartStart(size: number, { publicKey, contentType, fileName
|
|
|
262
267
|
export declare type MultipartUploadOptions = {
|
|
263
268
|
publicKey?: string;
|
|
264
269
|
signal?: AbortSignal;
|
|
265
|
-
onProgress?: ProgressCallback
|
|
270
|
+
onProgress?: ProgressCallback<ComputableProgressInfo>;
|
|
266
271
|
integration?: string;
|
|
267
272
|
retryThrottledRequestMaxTimes?: number;
|
|
268
273
|
};
|
|
@@ -345,7 +350,7 @@ export declare type MultipartOptions = {
|
|
|
345
350
|
secureExpire?: string;
|
|
346
351
|
store?: boolean;
|
|
347
352
|
signal?: AbortSignal;
|
|
348
|
-
onProgress?: ProgressCallback
|
|
353
|
+
onProgress?: ProgressCallback<ComputableProgressInfo>;
|
|
349
354
|
source?: string;
|
|
350
355
|
integration?: string;
|
|
351
356
|
userAgent?: CustomUserAgent;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uploadcare/upload-client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Library for work with Uploadcare Upload API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -14,13 +14,6 @@
|
|
|
14
14
|
"./dist/index.cjs": "./dist/index.browser.cjs",
|
|
15
15
|
"./dist/index.js": "./dist/index.browser.js"
|
|
16
16
|
},
|
|
17
|
-
"exports": {
|
|
18
|
-
"./package.json": "./package.json",
|
|
19
|
-
".": {
|
|
20
|
-
"require": "./dist/index.cjs",
|
|
21
|
-
"import": "./dist/index.js"
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
17
|
"react-native": {
|
|
25
18
|
"./lib/request/request.node.js": "./lib/request/request.browser.js",
|
|
26
19
|
"./lib/tools/getFormData.node.js": "./lib/tools/getFormData.react-native.js",
|
|
@@ -29,7 +22,7 @@
|
|
|
29
22
|
},
|
|
30
23
|
"scripts": {
|
|
31
24
|
"check-env-vars": "node ./checkvars.js",
|
|
32
|
-
"mock:start": "node --loader ts-node/esm
|
|
25
|
+
"mock:start": "node --loader ts-node/esm ./mock-server/server.ts --silent",
|
|
33
26
|
"clean": "rimraf dist",
|
|
34
27
|
"lint": "eslint ./src ./mock-server --ext=ts",
|
|
35
28
|
"test": "start-server-and-test mock:start :3000 test:jest",
|
|
@@ -98,7 +91,7 @@
|
|
|
98
91
|
"shipjs": "0.24.0",
|
|
99
92
|
"start-server-and-test": "1.11.7",
|
|
100
93
|
"ts-jest": "26.4.1",
|
|
101
|
-
"ts-node": "
|
|
94
|
+
"ts-node": "10.5.0"
|
|
102
95
|
},
|
|
103
96
|
"dependencies": {
|
|
104
97
|
"abort-controller": "^3.0.0",
|