@uploadcare/upload-client 2.2.1-alpha.0 → 3.1.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/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
  }
@@ -144,31 +147,97 @@ function identity(obj) {
144
147
  const transformFile = identity;
145
148
  var getFormData = () => new NodeFormData();
146
149
 
147
- const isFileTuple = (tuple) => tuple[0] === 'file';
148
- function buildFormData(body) {
149
- const formData = getFormData();
150
- for (const tuple of body) {
151
- if (Array.isArray(tuple[1])) {
152
- // refactor this
153
- tuple[1].forEach((val) => val && formData.append(tuple[0] + '[]', `${val}`));
154
- }
155
- else if (isFileTuple(tuple)) {
156
- const name = tuple[2];
157
- const file = transformFile(tuple[1]); // lgtm[js/superfluous-trailing-arguments]
158
- formData.append(tuple[0], file, name);
159
- }
160
- else if (tuple[1] != null) {
161
- formData.append(tuple[0], `${tuple[1]}`);
150
+ /**
151
+ * FileData type guard.
152
+ */
153
+ const isFileData = (data) => {
154
+ return (data !== undefined &&
155
+ ((typeof Blob !== 'undefined' && data instanceof Blob) ||
156
+ (typeof File !== 'undefined' && data instanceof File) ||
157
+ (typeof Buffer !== 'undefined' && data instanceof Buffer)));
158
+ };
159
+ /**
160
+ * Uuid type guard.
161
+ */
162
+ const isUuid = (data) => {
163
+ const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
164
+ const regExp = new RegExp(UUID_REGEX);
165
+ return !isFileData(data) && regExp.test(data);
166
+ };
167
+ /**
168
+ * Url type guard.
169
+ *
170
+ * @param {NodeFile | BrowserFile | Url | Uuid} data
171
+ */
172
+ const isUrl = (data) => {
173
+ const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
174
+ const regExp = new RegExp(URL_REGEX);
175
+ return !isFileData(data) && regExp.test(data);
176
+ };
177
+
178
+ const isSimpleValue = (value) => {
179
+ return (typeof value === 'string' ||
180
+ typeof value === 'number' ||
181
+ typeof value === 'undefined');
182
+ };
183
+ const isObjectValue = (value) => {
184
+ return !!value && typeof value === 'object' && !Array.isArray(value);
185
+ };
186
+ const isFileValue = (value) => !!value &&
187
+ typeof value === 'object' &&
188
+ 'data' in value &&
189
+ isFileData(value.data);
190
+ function collectParams(params, inputKey, inputValue) {
191
+ if (isFileValue(inputValue)) {
192
+ const name = inputValue.name;
193
+ const file = transformFile(inputValue.data); // lgtm [js/superfluous-trailing-arguments]
194
+ params.push(name ? [inputKey, file, name] : [inputKey, file]);
195
+ }
196
+ else if (isObjectValue(inputValue)) {
197
+ for (const [key, value] of Object.entries(inputValue)) {
198
+ if (typeof value !== 'undefined') {
199
+ params.push([`${inputKey}[${key}]`, String(value)]);
200
+ }
162
201
  }
163
202
  }
203
+ else if (isSimpleValue(inputValue) && inputValue) {
204
+ params.push([inputKey, inputValue.toString()]);
205
+ }
206
+ }
207
+ function getFormDataParams(options) {
208
+ const params = [];
209
+ for (const [key, value] of Object.entries(options)) {
210
+ collectParams(params, key, value);
211
+ }
212
+ return params;
213
+ }
214
+ function buildFormData(options) {
215
+ const formData = getFormData();
216
+ const params = getFormDataParams(options);
217
+ for (const param of params) {
218
+ formData.append(...param);
219
+ }
164
220
  return formData;
165
221
  }
166
222
 
167
223
  const serializePair = (key, value) => typeof value !== 'undefined' ? `${key}=${encodeURIComponent(value)}` : null;
224
+ // TODO: generalize value transforming logic and use it here and inside `buildFormData`
168
225
  const createQuery = (query) => Object.entries(query)
169
- .reduce((params, [key, value]) => params.concat(Array.isArray(value)
170
- ? value.map((value) => serializePair(`${key}[]`, value))
171
- : serializePair(key, value)), [])
226
+ .reduce((params, [key, value]) => {
227
+ let param;
228
+ if (typeof value === 'object' && !Array.isArray(value)) {
229
+ param = Object.entries(value)
230
+ .filter((entry) => typeof entry[1] !== 'undefined')
231
+ .map((entry) => serializePair(`${key}[${entry[0]}]`, String(entry[1])));
232
+ }
233
+ else if (Array.isArray(value)) {
234
+ param = value.map((val) => serializePair(`${key}[]`, val));
235
+ }
236
+ else {
237
+ param = serializePair(key, value);
238
+ }
239
+ return params.concat(param);
240
+ }, [])
172
241
  .filter((x) => !!x)
173
242
  .join('&');
174
243
  const getUrl = (base, path, query) => [
@@ -200,7 +269,7 @@ const defaultSettings = {
200
269
  const defaultContentType = 'application/octet-stream';
201
270
  const defaultFilename = 'original';
202
271
 
203
- var version = '2.2.0';
272
+ var version = '3.1.0';
204
273
 
205
274
  /**
206
275
  * Returns User Agent based on version and settings.
@@ -300,11 +369,15 @@ function retryIfThrottled(fn, retryThrottledMaxTimes) {
300
369
  }));
301
370
  }
302
371
 
372
+ function getStoreValue(store) {
373
+ return typeof store === 'undefined' ? 'auto' : store ? '1' : '0';
374
+ }
375
+
303
376
  /**
304
377
  * Performs file uploading request to Uploadcare Upload API.
305
378
  * Can be canceled and has progress.
306
379
  */
307
- function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
380
+ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
308
381
  return retryIfThrottled(() => {
309
382
  var _a;
310
383
  return request({
@@ -315,17 +388,18 @@ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, se
315
388
  headers: {
316
389
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
317
390
  },
318
- data: buildFormData([
319
- ['file', file, (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename],
320
- ['UPLOADCARE_PUB_KEY', publicKey],
321
- [
322
- 'UPLOADCARE_STORE',
323
- typeof store === 'undefined' ? 'auto' : store ? 1 : 0
324
- ],
325
- ['signature', secureSignature],
326
- ['expire', secureExpire],
327
- ['source', source]
328
- ]),
391
+ data: buildFormData({
392
+ file: {
393
+ data: file,
394
+ name: (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename
395
+ },
396
+ UPLOADCARE_PUB_KEY: publicKey,
397
+ UPLOADCARE_STORE: getStoreValue(store),
398
+ signature: secureSignature,
399
+ expire: secureExpire,
400
+ source: source,
401
+ metadata
402
+ }),
329
403
  signal,
330
404
  onProgress
331
405
  }).then(({ data, headers, request }) => {
@@ -348,7 +422,7 @@ var TypeEnum;
348
422
  /**
349
423
  * Uploading files from URL.
350
424
  */
351
- function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, store, fileName, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, source = 'url', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
425
+ function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, store, fileName, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, source = 'url', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
352
426
  return retryIfThrottled(() => request({
353
427
  method: 'POST',
354
428
  headers: {
@@ -358,13 +432,14 @@ function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, stor
358
432
  jsonerrors: 1,
359
433
  pub_key: publicKey,
360
434
  source_url: sourceUrl,
361
- store: typeof store === 'undefined' ? 'auto' : store ? 1 : undefined,
435
+ store: getStoreValue(store),
362
436
  filename: fileName,
363
437
  check_URL_duplicates: checkForUrlDuplicates ? 1 : undefined,
364
438
  save_URL_duplicates: saveUrlForRecurrentUploads ? 1 : undefined,
365
439
  signature: secureSignature,
366
440
  expire: secureExpire,
367
- source: source
441
+ source: source,
442
+ metadata
368
443
  }),
369
444
  signal
370
445
  }).then(({ data, headers, request }) => {
@@ -507,24 +582,25 @@ function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, sour
507
582
  /**
508
583
  * Start multipart uploading.
509
584
  */
510
- function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
585
+ function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
511
586
  return retryIfThrottled(() => request({
512
587
  method: 'POST',
513
588
  url: getUrl(baseURL, '/multipart/start/', { jsonerrors: 1 }),
514
589
  headers: {
515
590
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
516
591
  },
517
- data: buildFormData([
518
- ['filename', fileName !== null && fileName !== void 0 ? fileName : defaultFilename],
519
- ['size', size],
520
- ['content_type', contentType !== null && contentType !== void 0 ? contentType : defaultContentType],
521
- ['part_size', multipartChunkSize],
522
- ['UPLOADCARE_STORE', store ? '' : 'auto'],
523
- ['UPLOADCARE_PUB_KEY', publicKey],
524
- ['signature', secureSignature],
525
- ['expire', secureExpire],
526
- ['source', source]
527
- ]),
592
+ data: buildFormData({
593
+ filename: fileName !== null && fileName !== void 0 ? fileName : defaultFilename,
594
+ size: size,
595
+ content_type: contentType !== null && contentType !== void 0 ? contentType : defaultContentType,
596
+ part_size: multipartChunkSize,
597
+ UPLOADCARE_STORE: getStoreValue(store),
598
+ UPLOADCARE_PUB_KEY: publicKey,
599
+ signature: secureSignature,
600
+ expire: secureExpire,
601
+ source: source,
602
+ metadata
603
+ }),
528
604
  signal
529
605
  }).then(({ data, headers, request }) => {
530
606
  const response = camelizeKeys(JSON.parse(data));
@@ -547,13 +623,17 @@ function multipartUpload(part, url, { signal, onProgress }) {
547
623
  method: 'PUT',
548
624
  url,
549
625
  data: part,
550
- onProgress,
626
+ // Upload request can't be non-computable because we always know exact size
627
+ onProgress: onProgress,
551
628
  signal
552
629
  })
553
630
  .then((result) => {
554
631
  // hack for node ¯\_(ツ)_/¯
555
632
  if (onProgress)
556
- onProgress({ value: 1 });
633
+ onProgress({
634
+ isComputable: true,
635
+ value: 1
636
+ });
557
637
  return result;
558
638
  })
559
639
  .then(({ status }) => ({ code: status }));
@@ -569,11 +649,11 @@ function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL,
569
649
  headers: {
570
650
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
571
651
  },
572
- data: buildFormData([
573
- ['uuid', uuid],
574
- ['UPLOADCARE_PUB_KEY', publicKey],
575
- ['source', source]
576
- ]),
652
+ data: buildFormData({
653
+ uuid: uuid,
654
+ UPLOADCARE_PUB_KEY: publicKey,
655
+ source: source
656
+ }),
577
657
  signal
578
658
  }).then(({ data, headers, request }) => {
579
659
  const response = camelizeKeys(JSON.parse(data));
@@ -599,6 +679,8 @@ class UploadcareFile {
599
679
  this.originalFilename = null;
600
680
  this.imageInfo = null;
601
681
  this.videoInfo = null;
682
+ this.contentInfo = null;
683
+ this.metadata = null;
602
684
  const { uuid, s3Bucket } = fileInfo;
603
685
  const urlBase = s3Bucket
604
686
  ? `https://${s3Bucket}.s3.amazonaws.com/${uuid}/${fileInfo.filename}`
@@ -618,6 +700,8 @@ class UploadcareFile {
618
700
  this.originalFilename = fileInfo.originalFilename;
619
701
  this.imageInfo = camelizeKeys(fileInfo.imageInfo);
620
702
  this.videoInfo = camelizeKeys(fileInfo.videoInfo);
703
+ this.contentInfo = camelizeKeys(fileInfo.contentInfo);
704
+ this.metadata = fileInfo.metadata || null;
621
705
  }
622
706
  }
623
707
 
@@ -662,14 +746,14 @@ function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent,
662
746
  if (response.isReady) {
663
747
  return response;
664
748
  }
665
- onProgress && onProgress({ value: 1 });
749
+ onProgress && onProgress({ isComputable: true, value: 1 });
666
750
  return false;
667
751
  }),
668
752
  signal
669
753
  });
670
754
  }
671
755
 
672
- const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN }) => {
756
+ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN, metadata }) => {
673
757
  return base(file, {
674
758
  publicKey,
675
759
  fileName,
@@ -682,7 +766,8 @@ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature,
682
766
  source,
683
767
  integration,
684
768
  userAgent,
685
- retryThrottledRequestMaxTimes
769
+ retryThrottledRequestMaxTimes,
770
+ metadata
686
771
  })
687
772
  .then(({ file }) => {
688
773
  return isReadyPoll({
@@ -902,13 +987,25 @@ function pollStrategy({ token, publicKey, baseURL, integration, userAgent, retry
902
987
  return new UploadClientError(`Token "${token}" was not found.`);
903
988
  }
904
989
  case Status.Progress: {
905
- if (onProgress)
906
- onProgress({ value: response.done / response.total });
990
+ if (onProgress) {
991
+ if (response.total === 'unknown') {
992
+ onProgress({ isComputable: false });
993
+ }
994
+ else {
995
+ onProgress({
996
+ isComputable: true,
997
+ value: response.done / response.total
998
+ });
999
+ }
1000
+ }
907
1001
  return false;
908
1002
  }
909
1003
  case Status.Success: {
910
1004
  if (onProgress)
911
- onProgress({ value: response.done / response.total });
1005
+ onProgress({
1006
+ isComputable: true,
1007
+ value: response.done / response.total
1008
+ });
912
1009
  return response;
913
1010
  }
914
1011
  default: {
@@ -934,14 +1031,25 @@ const pushStrategy = ({ token, pusherKey, signal, onProgress }) => new Promise((
934
1031
  switch (result.status) {
935
1032
  case Status.Progress: {
936
1033
  if (onProgress) {
937
- onProgress({ value: result.done / result.total });
1034
+ if (result.total === 'unknown') {
1035
+ onProgress({ isComputable: false });
1036
+ }
1037
+ else {
1038
+ onProgress({
1039
+ isComputable: true,
1040
+ value: result.done / result.total
1041
+ });
1042
+ }
938
1043
  }
939
1044
  break;
940
1045
  }
941
1046
  case Status.Success: {
942
1047
  destroy();
943
1048
  if (onProgress)
944
- onProgress({ value: result.done / result.total });
1049
+ onProgress({
1050
+ isComputable: true,
1051
+ value: result.done / result.total
1052
+ });
945
1053
  resolve(result);
946
1054
  break;
947
1055
  }
@@ -952,7 +1060,7 @@ const pushStrategy = ({ token, pusherKey, signal, onProgress }) => new Promise((
952
1060
  }
953
1061
  });
954
1062
  });
955
- const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, pusherKey = defaultSettings.pusherKey }) => Promise.resolve(preconnect(pusherKey))
1063
+ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, pusherKey = defaultSettings.pusherKey, metadata }) => Promise.resolve(preconnect(pusherKey))
956
1064
  .then(() => fromUrl(sourceUrl, {
957
1065
  publicKey,
958
1066
  fileName,
@@ -966,7 +1074,8 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
966
1074
  source,
967
1075
  integration,
968
1076
  userAgent,
969
- retryThrottledRequestMaxTimes
1077
+ retryThrottledRequestMaxTimes,
1078
+ metadata
970
1079
  }))
971
1080
  .catch((error) => {
972
1081
  const pusher = getPusher(pusherKey);
@@ -1029,39 +1138,14 @@ const uploadFromUploaded = (uuid, { publicKey, fileName, baseURL, signal, onProg
1029
1138
  .then((result) => {
1030
1139
  // hack for node ¯\_(ツ)_/¯
1031
1140
  if (onProgress)
1032
- onProgress({ value: 1 });
1141
+ onProgress({
1142
+ isComputable: true,
1143
+ value: 1
1144
+ });
1033
1145
  return result;
1034
1146
  });
1035
1147
  };
1036
1148
 
1037
- /**
1038
- * FileData type guard.
1039
- */
1040
- const isFileData = (data) => {
1041
- return (data !== undefined &&
1042
- ((typeof Blob !== 'undefined' && data instanceof Blob) ||
1043
- (typeof File !== 'undefined' && data instanceof File) ||
1044
- (typeof Buffer !== 'undefined' && data instanceof Buffer)));
1045
- };
1046
- /**
1047
- * Uuid type guard.
1048
- */
1049
- const isUuid = (data) => {
1050
- const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
1051
- const regExp = new RegExp(UUID_REGEX);
1052
- return !isFileData(data) && regExp.test(data);
1053
- };
1054
- /**
1055
- * Url type guard.
1056
- *
1057
- * @param {NodeFile | BrowserFile | Url | Uuid} data
1058
- */
1059
- const isUrl = (data) => {
1060
- const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
1061
- const regExp = new RegExp(URL_REGEX);
1062
- return !isFileData(data) && regExp.test(data);
1063
- };
1064
-
1065
1149
  /**
1066
1150
  * Get file size.
1067
1151
  */
@@ -1131,19 +1215,25 @@ const uploadPartWithRetry = (chunk, url, { publicKey, onProgress, signal, integr
1131
1215
  }
1132
1216
  throw error;
1133
1217
  }));
1134
- 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 }) => {
1218
+ 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, metadata }) => {
1135
1219
  const size = fileSize || getFileSize(file);
1136
1220
  let progressValues;
1137
- const createProgressHandler = (size, index) => {
1221
+ const createProgressHandler = (totalChunks, chunkIdx) => {
1138
1222
  if (!onProgress)
1139
1223
  return;
1140
1224
  if (!progressValues) {
1141
- progressValues = Array(size).fill(0);
1225
+ progressValues = Array(totalChunks).fill(0);
1142
1226
  }
1143
1227
  const sum = (values) => values.reduce((sum, next) => sum + next, 0);
1144
- return ({ value }) => {
1145
- progressValues[index] = value;
1146
- onProgress({ value: sum(progressValues) / size });
1228
+ return (info) => {
1229
+ if (!info.isComputable) {
1230
+ return;
1231
+ }
1232
+ progressValues[chunkIdx] = info.value;
1233
+ onProgress({
1234
+ isComputable: true,
1235
+ value: sum(progressValues) / totalChunks
1236
+ });
1147
1237
  };
1148
1238
  };
1149
1239
  return multipartStart(size, {
@@ -1158,7 +1248,8 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1158
1248
  source,
1159
1249
  integration,
1160
1250
  userAgent,
1161
- retryThrottledRequestMaxTimes
1251
+ retryThrottledRequestMaxTimes,
1252
+ metadata
1162
1253
  })
1163
1254
  .then(({ uuid, parts }) => {
1164
1255
  const getChunk = prepareChunks(file, size, multipartChunkSize);
@@ -1205,10 +1296,10 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1205
1296
  /**
1206
1297
  * Uploads file from provided data.
1207
1298
  */
1208
- function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartChunkSize, multipartMaxAttempts, maxConcurrentRequests, baseCDN = defaultSettings.baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, pusherKey }) {
1299
+ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartMinFileSize, multipartChunkSize, multipartMaxAttempts, maxConcurrentRequests, baseCDN = defaultSettings.baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, pusherKey, metadata }) {
1209
1300
  if (isFileData(data)) {
1210
1301
  const fileSize = getFileSize(data);
1211
- if (isMultipart(fileSize)) {
1302
+ if (isMultipart(fileSize, multipartMinFileSize)) {
1212
1303
  return uploadMultipart(data, {
1213
1304
  publicKey,
1214
1305
  contentType,
@@ -1226,7 +1317,8 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1226
1317
  userAgent,
1227
1318
  maxConcurrentRequests,
1228
1319
  retryThrottledRequestMaxTimes,
1229
- baseCDN
1320
+ baseCDN,
1321
+ metadata
1230
1322
  });
1231
1323
  }
1232
1324
  return uploadFromObject(data, {
@@ -1242,7 +1334,8 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1242
1334
  integration,
1243
1335
  userAgent,
1244
1336
  retryThrottledRequestMaxTimes,
1245
- baseCDN
1337
+ baseCDN,
1338
+ metadata
1246
1339
  });
1247
1340
  }
1248
1341
  if (isUrl(data)) {
@@ -1262,7 +1355,8 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1262
1355
  integration,
1263
1356
  userAgent,
1264
1357
  retryThrottledRequestMaxTimes,
1265
- pusherKey
1358
+ pusherKey,
1359
+ metadata
1266
1360
  });
1267
1361
  }
1268
1362
  if (isUuid(data)) {
@@ -1336,6 +1430,7 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1336
1430
  throw new TypeError(`Group uploading from "${data}" is not supported`);
1337
1431
  }
1338
1432
  let progressValues;
1433
+ let isStillComputable = true;
1339
1434
  const filesCount = data.length;
1340
1435
  const createProgressHandler = (size, index) => {
1341
1436
  if (!onProgress)
@@ -1344,9 +1439,14 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1344
1439
  progressValues = Array(size).fill(0);
1345
1440
  }
1346
1441
  const normalize = (values) => values.reduce((sum, next) => sum + next) / size;
1347
- return ({ value }) => {
1348
- progressValues[index] = value;
1349
- onProgress({ value: normalize(progressValues) });
1442
+ return (info) => {
1443
+ if (!info.isComputable || !isStillComputable) {
1444
+ isStillComputable = false;
1445
+ onProgress({ isComputable: false });
1446
+ return;
1447
+ }
1448
+ progressValues[index] = info.value;
1449
+ onProgress({ isComputable: true, value: normalize(progressValues) });
1350
1450
  };
1351
1451
  };
1352
1452
  return Promise.all(data.map((file, index) => uploadFile(file, {
@@ -1385,7 +1485,12 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1385
1485
  integration,
1386
1486
  userAgent,
1387
1487
  retryThrottledRequestMaxTimes
1388
- }).then((groupInfo) => new UploadcareGroup(groupInfo, filesInGroup));
1488
+ })
1489
+ .then((groupInfo) => new UploadcareGroup(groupInfo, filesInGroup))
1490
+ .then((group) => {
1491
+ onProgress && onProgress({ isComputable: true, value: 1 });
1492
+ return group;
1493
+ });
1389
1494
  });
1390
1495
  }
1391
1496