@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.
@@ -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) {
@@ -112,31 +120,97 @@ function identity(obj) {
112
120
  const transformFile = identity;
113
121
  var getFormData = () => new FormData();
114
122
 
115
- const isFileTuple = (tuple) => tuple[0] === 'file';
116
- function buildFormData(body) {
117
- const formData = getFormData();
118
- for (const tuple of body) {
119
- if (Array.isArray(tuple[1])) {
120
- // refactor this
121
- tuple[1].forEach((val) => val && formData.append(tuple[0] + '[]', `${val}`));
122
- }
123
- else if (isFileTuple(tuple)) {
124
- const name = tuple[2];
125
- const file = transformFile(tuple[1]); // lgtm[js/superfluous-trailing-arguments]
126
- formData.append(tuple[0], file, name);
127
- }
128
- else if (tuple[1] != null) {
129
- formData.append(tuple[0], `${tuple[1]}`);
123
+ /**
124
+ * FileData type guard.
125
+ */
126
+ const isFileData = (data) => {
127
+ return (data !== undefined &&
128
+ ((typeof Blob !== 'undefined' && data instanceof Blob) ||
129
+ (typeof File !== 'undefined' && data instanceof File) ||
130
+ (typeof Buffer !== 'undefined' && data instanceof Buffer)));
131
+ };
132
+ /**
133
+ * Uuid type guard.
134
+ */
135
+ const isUuid = (data) => {
136
+ const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
137
+ const regExp = new RegExp(UUID_REGEX);
138
+ return !isFileData(data) && regExp.test(data);
139
+ };
140
+ /**
141
+ * Url type guard.
142
+ *
143
+ * @param {NodeFile | BrowserFile | Url | Uuid} data
144
+ */
145
+ const isUrl = (data) => {
146
+ const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
147
+ const regExp = new RegExp(URL_REGEX);
148
+ return !isFileData(data) && regExp.test(data);
149
+ };
150
+
151
+ const isSimpleValue = (value) => {
152
+ return (typeof value === 'string' ||
153
+ typeof value === 'number' ||
154
+ typeof value === 'undefined');
155
+ };
156
+ const isObjectValue = (value) => {
157
+ return !!value && typeof value === 'object' && !Array.isArray(value);
158
+ };
159
+ const isFileValue = (value) => !!value &&
160
+ typeof value === 'object' &&
161
+ 'data' in value &&
162
+ isFileData(value.data);
163
+ function collectParams(params, inputKey, inputValue) {
164
+ if (isFileValue(inputValue)) {
165
+ const name = inputValue.name;
166
+ const file = transformFile(inputValue.data); // lgtm [js/superfluous-trailing-arguments]
167
+ params.push(name ? [inputKey, file, name] : [inputKey, file]);
168
+ }
169
+ else if (isObjectValue(inputValue)) {
170
+ for (const [key, value] of Object.entries(inputValue)) {
171
+ if (typeof value !== 'undefined') {
172
+ params.push([`${inputKey}[${key}]`, String(value)]);
173
+ }
130
174
  }
131
175
  }
176
+ else if (isSimpleValue(inputValue) && inputValue) {
177
+ params.push([inputKey, inputValue.toString()]);
178
+ }
179
+ }
180
+ function getFormDataParams(options) {
181
+ const params = [];
182
+ for (const [key, value] of Object.entries(options)) {
183
+ collectParams(params, key, value);
184
+ }
185
+ return params;
186
+ }
187
+ function buildFormData(options) {
188
+ const formData = getFormData();
189
+ const params = getFormDataParams(options);
190
+ for (const param of params) {
191
+ formData.append(...param);
192
+ }
132
193
  return formData;
133
194
  }
134
195
 
135
196
  const serializePair = (key, value) => typeof value !== 'undefined' ? `${key}=${encodeURIComponent(value)}` : null;
197
+ // TODO: generalize value transforming logic and use it here and inside `buildFormData`
136
198
  const createQuery = (query) => Object.entries(query)
137
- .reduce((params, [key, value]) => params.concat(Array.isArray(value)
138
- ? value.map((value) => serializePair(`${key}[]`, value))
139
- : serializePair(key, value)), [])
199
+ .reduce((params, [key, value]) => {
200
+ let param;
201
+ if (typeof value === 'object' && !Array.isArray(value)) {
202
+ param = Object.entries(value)
203
+ .filter((entry) => typeof entry[1] !== 'undefined')
204
+ .map((entry) => serializePair(`${key}[${entry[0]}]`, String(entry[1])));
205
+ }
206
+ else if (Array.isArray(value)) {
207
+ param = value.map((val) => serializePair(`${key}[]`, val));
208
+ }
209
+ else {
210
+ param = serializePair(key, value);
211
+ }
212
+ return params.concat(param);
213
+ }, [])
140
214
  .filter((x) => !!x)
141
215
  .join('&');
142
216
  const getUrl = (base, path, query) => [
@@ -168,7 +242,7 @@ const defaultSettings = {
168
242
  const defaultContentType = 'application/octet-stream';
169
243
  const defaultFilename = 'original';
170
244
 
171
- var version = '2.2.0';
245
+ var version = '3.1.0';
172
246
 
173
247
  /**
174
248
  * Returns User Agent based on version and settings.
@@ -268,11 +342,15 @@ function retryIfThrottled(fn, retryThrottledMaxTimes) {
268
342
  }));
269
343
  }
270
344
 
345
+ function getStoreValue(store) {
346
+ return typeof store === 'undefined' ? 'auto' : store ? '1' : '0';
347
+ }
348
+
271
349
  /**
272
350
  * Performs file uploading request to Uploadcare Upload API.
273
351
  * Can be canceled and has progress.
274
352
  */
275
- function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
353
+ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
276
354
  return retryIfThrottled(() => {
277
355
  var _a;
278
356
  return request({
@@ -283,17 +361,18 @@ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, se
283
361
  headers: {
284
362
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
285
363
  },
286
- data: buildFormData([
287
- ['file', file, (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename],
288
- ['UPLOADCARE_PUB_KEY', publicKey],
289
- [
290
- 'UPLOADCARE_STORE',
291
- typeof store === 'undefined' ? 'auto' : store ? 1 : 0
292
- ],
293
- ['signature', secureSignature],
294
- ['expire', secureExpire],
295
- ['source', source]
296
- ]),
364
+ data: buildFormData({
365
+ file: {
366
+ data: file,
367
+ name: (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename
368
+ },
369
+ UPLOADCARE_PUB_KEY: publicKey,
370
+ UPLOADCARE_STORE: getStoreValue(store),
371
+ signature: secureSignature,
372
+ expire: secureExpire,
373
+ source: source,
374
+ metadata
375
+ }),
297
376
  signal,
298
377
  onProgress
299
378
  }).then(({ data, headers, request }) => {
@@ -316,7 +395,7 @@ var TypeEnum;
316
395
  /**
317
396
  * Uploading files from URL.
318
397
  */
319
- function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, store, fileName, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, source = 'url', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
398
+ function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, store, fileName, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, source = 'url', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
320
399
  return retryIfThrottled(() => request({
321
400
  method: 'POST',
322
401
  headers: {
@@ -326,13 +405,14 @@ function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, stor
326
405
  jsonerrors: 1,
327
406
  pub_key: publicKey,
328
407
  source_url: sourceUrl,
329
- store: typeof store === 'undefined' ? 'auto' : store ? 1 : undefined,
408
+ store: getStoreValue(store),
330
409
  filename: fileName,
331
410
  check_URL_duplicates: checkForUrlDuplicates ? 1 : undefined,
332
411
  save_URL_duplicates: saveUrlForRecurrentUploads ? 1 : undefined,
333
412
  signature: secureSignature,
334
413
  expire: secureExpire,
335
- source: source
414
+ source: source,
415
+ metadata
336
416
  }),
337
417
  signal
338
418
  }).then(({ data, headers, request }) => {
@@ -475,24 +555,25 @@ function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, sour
475
555
  /**
476
556
  * Start multipart uploading.
477
557
  */
478
- function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
558
+ function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
479
559
  return retryIfThrottled(() => request({
480
560
  method: 'POST',
481
561
  url: getUrl(baseURL, '/multipart/start/', { jsonerrors: 1 }),
482
562
  headers: {
483
563
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
484
564
  },
485
- data: buildFormData([
486
- ['filename', fileName !== null && fileName !== void 0 ? fileName : defaultFilename],
487
- ['size', size],
488
- ['content_type', contentType !== null && contentType !== void 0 ? contentType : defaultContentType],
489
- ['part_size', multipartChunkSize],
490
- ['UPLOADCARE_STORE', store ? '' : 'auto'],
491
- ['UPLOADCARE_PUB_KEY', publicKey],
492
- ['signature', secureSignature],
493
- ['expire', secureExpire],
494
- ['source', source]
495
- ]),
565
+ data: buildFormData({
566
+ filename: fileName !== null && fileName !== void 0 ? fileName : defaultFilename,
567
+ size: size,
568
+ content_type: contentType !== null && contentType !== void 0 ? contentType : defaultContentType,
569
+ part_size: multipartChunkSize,
570
+ UPLOADCARE_STORE: getStoreValue(store),
571
+ UPLOADCARE_PUB_KEY: publicKey,
572
+ signature: secureSignature,
573
+ expire: secureExpire,
574
+ source: source,
575
+ metadata
576
+ }),
496
577
  signal
497
578
  }).then(({ data, headers, request }) => {
498
579
  const response = camelizeKeys(JSON.parse(data));
@@ -515,13 +596,17 @@ function multipartUpload(part, url, { signal, onProgress }) {
515
596
  method: 'PUT',
516
597
  url,
517
598
  data: part,
518
- onProgress,
599
+ // Upload request can't be non-computable because we always know exact size
600
+ onProgress: onProgress,
519
601
  signal
520
602
  })
521
603
  .then((result) => {
522
604
  // hack for node ¯\_(ツ)_/¯
523
605
  if (onProgress)
524
- onProgress({ value: 1 });
606
+ onProgress({
607
+ isComputable: true,
608
+ value: 1
609
+ });
525
610
  return result;
526
611
  })
527
612
  .then(({ status }) => ({ code: status }));
@@ -537,11 +622,11 @@ function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL,
537
622
  headers: {
538
623
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
539
624
  },
540
- data: buildFormData([
541
- ['uuid', uuid],
542
- ['UPLOADCARE_PUB_KEY', publicKey],
543
- ['source', source]
544
- ]),
625
+ data: buildFormData({
626
+ uuid: uuid,
627
+ UPLOADCARE_PUB_KEY: publicKey,
628
+ source: source
629
+ }),
545
630
  signal
546
631
  }).then(({ data, headers, request }) => {
547
632
  const response = camelizeKeys(JSON.parse(data));
@@ -567,6 +652,8 @@ class UploadcareFile {
567
652
  this.originalFilename = null;
568
653
  this.imageInfo = null;
569
654
  this.videoInfo = null;
655
+ this.contentInfo = null;
656
+ this.metadata = null;
570
657
  const { uuid, s3Bucket } = fileInfo;
571
658
  const urlBase = s3Bucket
572
659
  ? `https://${s3Bucket}.s3.amazonaws.com/${uuid}/${fileInfo.filename}`
@@ -586,6 +673,8 @@ class UploadcareFile {
586
673
  this.originalFilename = fileInfo.originalFilename;
587
674
  this.imageInfo = camelizeKeys(fileInfo.imageInfo);
588
675
  this.videoInfo = camelizeKeys(fileInfo.videoInfo);
676
+ this.contentInfo = camelizeKeys(fileInfo.contentInfo);
677
+ this.metadata = fileInfo.metadata || null;
589
678
  }
590
679
  }
591
680
 
@@ -630,14 +719,14 @@ function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent,
630
719
  if (response.isReady) {
631
720
  return response;
632
721
  }
633
- onProgress && onProgress({ value: 1 });
722
+ onProgress && onProgress({ isComputable: true, value: 1 });
634
723
  return false;
635
724
  }),
636
725
  signal
637
726
  });
638
727
  }
639
728
 
640
- const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN }) => {
729
+ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN, metadata }) => {
641
730
  return base(file, {
642
731
  publicKey,
643
732
  fileName,
@@ -650,7 +739,8 @@ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature,
650
739
  source,
651
740
  integration,
652
741
  userAgent,
653
- retryThrottledRequestMaxTimes
742
+ retryThrottledRequestMaxTimes,
743
+ metadata
654
744
  })
655
745
  .then(({ file }) => {
656
746
  return isReadyPoll({
@@ -880,13 +970,25 @@ function pollStrategy({ token, publicKey, baseURL, integration, userAgent, retry
880
970
  return new UploadClientError(`Token "${token}" was not found.`);
881
971
  }
882
972
  case Status.Progress: {
883
- if (onProgress)
884
- onProgress({ value: response.done / response.total });
973
+ if (onProgress) {
974
+ if (response.total === 'unknown') {
975
+ onProgress({ isComputable: false });
976
+ }
977
+ else {
978
+ onProgress({
979
+ isComputable: true,
980
+ value: response.done / response.total
981
+ });
982
+ }
983
+ }
885
984
  return false;
886
985
  }
887
986
  case Status.Success: {
888
987
  if (onProgress)
889
- onProgress({ value: response.done / response.total });
988
+ onProgress({
989
+ isComputable: true,
990
+ value: response.done / response.total
991
+ });
890
992
  return response;
891
993
  }
892
994
  default: {
@@ -912,14 +1014,25 @@ const pushStrategy = ({ token, pusherKey, signal, onProgress }) => new Promise((
912
1014
  switch (result.status) {
913
1015
  case Status.Progress: {
914
1016
  if (onProgress) {
915
- onProgress({ value: result.done / result.total });
1017
+ if (result.total === 'unknown') {
1018
+ onProgress({ isComputable: false });
1019
+ }
1020
+ else {
1021
+ onProgress({
1022
+ isComputable: true,
1023
+ value: result.done / result.total
1024
+ });
1025
+ }
916
1026
  }
917
1027
  break;
918
1028
  }
919
1029
  case Status.Success: {
920
1030
  destroy();
921
1031
  if (onProgress)
922
- onProgress({ value: result.done / result.total });
1032
+ onProgress({
1033
+ isComputable: true,
1034
+ value: result.done / result.total
1035
+ });
923
1036
  resolve(result);
924
1037
  break;
925
1038
  }
@@ -930,7 +1043,7 @@ const pushStrategy = ({ token, pusherKey, signal, onProgress }) => new Promise((
930
1043
  }
931
1044
  });
932
1045
  });
933
- 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))
1046
+ 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))
934
1047
  .then(() => fromUrl(sourceUrl, {
935
1048
  publicKey,
936
1049
  fileName,
@@ -944,7 +1057,8 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
944
1057
  source,
945
1058
  integration,
946
1059
  userAgent,
947
- retryThrottledRequestMaxTimes
1060
+ retryThrottledRequestMaxTimes,
1061
+ metadata
948
1062
  }))
949
1063
  .catch((error) => {
950
1064
  const pusher = getPusher(pusherKey);
@@ -1007,39 +1121,14 @@ const uploadFromUploaded = (uuid, { publicKey, fileName, baseURL, signal, onProg
1007
1121
  .then((result) => {
1008
1122
  // hack for node ¯\_(ツ)_/¯
1009
1123
  if (onProgress)
1010
- onProgress({ value: 1 });
1124
+ onProgress({
1125
+ isComputable: true,
1126
+ value: 1
1127
+ });
1011
1128
  return result;
1012
1129
  });
1013
1130
  };
1014
1131
 
1015
- /**
1016
- * FileData type guard.
1017
- */
1018
- const isFileData = (data) => {
1019
- return (data !== undefined &&
1020
- ((typeof Blob !== 'undefined' && data instanceof Blob) ||
1021
- (typeof File !== 'undefined' && data instanceof File) ||
1022
- (typeof Buffer !== 'undefined' && data instanceof Buffer)));
1023
- };
1024
- /**
1025
- * Uuid type guard.
1026
- */
1027
- const isUuid = (data) => {
1028
- const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
1029
- const regExp = new RegExp(UUID_REGEX);
1030
- return !isFileData(data) && regExp.test(data);
1031
- };
1032
- /**
1033
- * Url type guard.
1034
- *
1035
- * @param {NodeFile | BrowserFile | Url | Uuid} data
1036
- */
1037
- const isUrl = (data) => {
1038
- const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
1039
- const regExp = new RegExp(URL_REGEX);
1040
- return !isFileData(data) && regExp.test(data);
1041
- };
1042
-
1043
1132
  /**
1044
1133
  * Get file size.
1045
1134
  */
@@ -1109,19 +1198,25 @@ const uploadPartWithRetry = (chunk, url, { publicKey, onProgress, signal, integr
1109
1198
  }
1110
1199
  throw error;
1111
1200
  }));
1112
- 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 }) => {
1201
+ 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 }) => {
1113
1202
  const size = fileSize || getFileSize(file);
1114
1203
  let progressValues;
1115
- const createProgressHandler = (size, index) => {
1204
+ const createProgressHandler = (totalChunks, chunkIdx) => {
1116
1205
  if (!onProgress)
1117
1206
  return;
1118
1207
  if (!progressValues) {
1119
- progressValues = Array(size).fill(0);
1208
+ progressValues = Array(totalChunks).fill(0);
1120
1209
  }
1121
1210
  const sum = (values) => values.reduce((sum, next) => sum + next, 0);
1122
- return ({ value }) => {
1123
- progressValues[index] = value;
1124
- onProgress({ value: sum(progressValues) / size });
1211
+ return (info) => {
1212
+ if (!info.isComputable) {
1213
+ return;
1214
+ }
1215
+ progressValues[chunkIdx] = info.value;
1216
+ onProgress({
1217
+ isComputable: true,
1218
+ value: sum(progressValues) / totalChunks
1219
+ });
1125
1220
  };
1126
1221
  };
1127
1222
  return multipartStart(size, {
@@ -1136,7 +1231,8 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1136
1231
  source,
1137
1232
  integration,
1138
1233
  userAgent,
1139
- retryThrottledRequestMaxTimes
1234
+ retryThrottledRequestMaxTimes,
1235
+ metadata
1140
1236
  })
1141
1237
  .then(({ uuid, parts }) => {
1142
1238
  const getChunk = prepareChunks(file, size, multipartChunkSize);
@@ -1183,10 +1279,10 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1183
1279
  /**
1184
1280
  * Uploads file from provided data.
1185
1281
  */
1186
- 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 }) {
1282
+ 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 }) {
1187
1283
  if (isFileData(data)) {
1188
1284
  const fileSize = getFileSize(data);
1189
- if (isMultipart(fileSize)) {
1285
+ if (isMultipart(fileSize, multipartMinFileSize)) {
1190
1286
  return uploadMultipart(data, {
1191
1287
  publicKey,
1192
1288
  contentType,
@@ -1204,7 +1300,8 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1204
1300
  userAgent,
1205
1301
  maxConcurrentRequests,
1206
1302
  retryThrottledRequestMaxTimes,
1207
- baseCDN
1303
+ baseCDN,
1304
+ metadata
1208
1305
  });
1209
1306
  }
1210
1307
  return uploadFromObject(data, {
@@ -1220,7 +1317,8 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1220
1317
  integration,
1221
1318
  userAgent,
1222
1319
  retryThrottledRequestMaxTimes,
1223
- baseCDN
1320
+ baseCDN,
1321
+ metadata
1224
1322
  });
1225
1323
  }
1226
1324
  if (isUrl(data)) {
@@ -1240,7 +1338,8 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1240
1338
  integration,
1241
1339
  userAgent,
1242
1340
  retryThrottledRequestMaxTimes,
1243
- pusherKey
1341
+ pusherKey,
1342
+ metadata
1244
1343
  });
1245
1344
  }
1246
1345
  if (isUuid(data)) {
@@ -1314,6 +1413,7 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1314
1413
  throw new TypeError(`Group uploading from "${data}" is not supported`);
1315
1414
  }
1316
1415
  let progressValues;
1416
+ let isStillComputable = true;
1317
1417
  const filesCount = data.length;
1318
1418
  const createProgressHandler = (size, index) => {
1319
1419
  if (!onProgress)
@@ -1322,9 +1422,14 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1322
1422
  progressValues = Array(size).fill(0);
1323
1423
  }
1324
1424
  const normalize = (values) => values.reduce((sum, next) => sum + next) / size;
1325
- return ({ value }) => {
1326
- progressValues[index] = value;
1327
- onProgress({ value: normalize(progressValues) });
1425
+ return (info) => {
1426
+ if (!info.isComputable || !isStillComputable) {
1427
+ isStillComputable = false;
1428
+ onProgress({ isComputable: false });
1429
+ return;
1430
+ }
1431
+ progressValues[index] = info.value;
1432
+ onProgress({ isComputable: true, value: normalize(progressValues) });
1328
1433
  };
1329
1434
  };
1330
1435
  return Promise.all(data.map((file, index) => uploadFile(file, {
@@ -1363,7 +1468,12 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1363
1468
  integration,
1364
1469
  userAgent,
1365
1470
  retryThrottledRequestMaxTimes
1366
- }).then((groupInfo) => new UploadcareGroup(groupInfo, filesInGroup));
1471
+ })
1472
+ .then((groupInfo) => new UploadcareGroup(groupInfo, filesInGroup))
1473
+ .then((group) => {
1474
+ onProgress && onProgress({ isComputable: true, value: 1 });
1475
+ return group;
1476
+ });
1367
1477
  });
1368
1478
  }
1369
1479