@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 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
- #### `integration: string | CustomUserAgentFn`
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://img.shields.io/github/workflow/status/uploadcare/uploadcare-upload-client/Ship%20js%20trigger
450
- [build-url]: https://github.com/uploadcare/uploadcare-upload-client/actions?query=workflow%3A%22Ship+js+trigger%22
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
@@ -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
- onProgress({ value: event.loaded / event.total });
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 = '2.2.0';
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({ value: 1 });
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
- onProgress({ value: response.done / response.total });
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({ value: response.done / response.total });
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
- onProgress({ value: result.done / result.total });
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({ value: result.done / result.total });
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({ value: 1 });
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 (size, index) {
1319
+ var createProgressHandler = function (totalChunks, chunkIdx) {
1282
1320
  if (!onProgress)
1283
1321
  return;
1284
1322
  if (!progressValues) {
1285
- progressValues = Array(size).fill(0);
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 (_a) {
1291
- var value = _a.value;
1292
- progressValues[index] = value;
1293
- onProgress({ value: sum(progressValues) / size });
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 (_a) {
1509
- var value = _a.value;
1510
- progressValues[index] = value;
1511
- onProgress({ value: normalize(progressValues) });
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
- }).then(function (groupInfo) { return new UploadcareGroup(groupInfo, filesInGroup); });
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
 
@@ -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
- onProgress({ value: event.loaded / event.total });
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 = '2.2.0';
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
- onProgress,
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({ value: 1 });
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
- onProgress({ value: response.done / response.total });
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({ value: response.done / response.total });
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
- onProgress({ value: result.done / result.total });
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({ value: result.done / result.total });
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({ value: 1 });
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 = (size, index) => {
1153
+ const createProgressHandler = (totalChunks, chunkIdx) => {
1116
1154
  if (!onProgress)
1117
1155
  return;
1118
1156
  if (!progressValues) {
1119
- progressValues = Array(size).fill(0);
1157
+ progressValues = Array(totalChunks).fill(0);
1120
1158
  }
1121
1159
  const sum = (values) => values.reduce((sum, next) => sum + next, 0);
1122
- return ({ value }) => {
1123
- progressValues[index] = value;
1124
- onProgress({ value: sum(progressValues) / size });
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 ({ value }) => {
1326
- progressValues[index] = value;
1327
- onProgress({ value: normalize(progressValues) });
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
- }).then((groupInfo) => new UploadcareGroup(groupInfo, filesInGroup));
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({ value: this._position / this.size });
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 = '2.2.0';
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({ value: 1 });
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
- onProgress({ value: response.done / response.total });
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({ value: response.done / response.total });
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
- onProgress({ value: result.done / result.total });
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({ value: result.done / result.total });
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({ value: 1 });
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 (size, index) {
1348
+ var createProgressHandler = function (totalChunks, chunkIdx) {
1316
1349
  if (!onProgress)
1317
1350
  return;
1318
1351
  if (!progressValues) {
1319
- progressValues = Array(size).fill(0);
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 (_a) {
1325
- var value = _a.value;
1326
- progressValues[index] = value;
1327
- onProgress({ value: sum(progressValues) / size });
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 (_a) {
1543
- var value = _a.value;
1544
- progressValues[index] = value;
1545
- onProgress({ value: normalize(progressValues) });
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
- }).then(function (groupInfo) { return new UploadcareGroup(groupInfo, filesInGroup); });
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({ value: this._position / this.size });
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 = '2.2.0';
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
- onProgress,
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({ value: 1 });
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
- onProgress({ value: response.done / response.total });
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({ value: response.done / response.total });
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
- onProgress({ value: result.done / result.total });
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({ value: result.done / result.total });
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({ value: 1 });
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 = (size, index) => {
1170
+ const createProgressHandler = (totalChunks, chunkIdx) => {
1138
1171
  if (!onProgress)
1139
1172
  return;
1140
1173
  if (!progressValues) {
1141
- progressValues = Array(size).fill(0);
1174
+ progressValues = Array(totalChunks).fill(0);
1142
1175
  }
1143
1176
  const sum = (values) => values.reduce((sum, next) => sum + next, 0);
1144
- return ({ value }) => {
1145
- progressValues[index] = value;
1146
- onProgress({ value: sum(progressValues) / size });
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 ({ value }) => {
1348
- progressValues[index] = value;
1349
- onProgress({ value: normalize(progressValues) });
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
- }).then((groupInfo) => new UploadcareGroup(groupInfo, filesInGroup));
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 ProgressCallback = (arg: {
73
+ export declare type ComputableProgressInfo = {
74
+ isComputable: true;
74
75
  value: number;
75
- }) => void;
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": "2.2.0",
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.mjs ./mock-server/server.ts --silent",
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": "9.1.1"
94
+ "ts-node": "10.5.0"
102
95
  },
103
96
  "dependencies": {
104
97
  "abort-controller": "^3.0.0",