@uploadcare/upload-client 2.0.0-alpha.6 → 2.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.
@@ -1,36 +1,9 @@
1
- /*globals self, window */
2
-
3
- /*eslint-disable @mysticatea/prettier */
4
- const { AbortController, AbortSignal } =
5
- typeof self !== "undefined" ? self :
6
- typeof window !== "undefined" ? window :
7
- /* otherwise */ undefined;
8
-
9
- /*
10
- Settings for future support:
11
- parallelDirectUploads: 10,
12
- */
13
- const defaultSettings = {
14
- baseCDN: 'https://ucarecdn.com',
15
- baseURL: 'https://upload.uploadcare.com',
16
- maxContentLength: 50 * 1024 * 1024,
17
- retryThrottledRequestMaxTimes: 1,
18
- multipartMinFileSize: 25 * 1024 * 1024,
19
- multipartChunkSize: 5 * 1024 * 1024,
20
- multipartMinLastPartSize: 1024 * 1024,
21
- maxConcurrentRequests: 4,
22
- multipartMaxAttempts: 3,
23
- pollingTimeoutMilliseconds: 10000,
24
- pusherKey: '79ae88bd931ea68464d9'
25
- };
26
- const defaultContentType = 'application/octet-stream';
27
- const defaultFilename = 'original';
28
-
29
1
  class UploadClientError extends Error {
30
- constructor(message, request, response, headers) {
2
+ constructor(message, code, request, response, headers) {
31
3
  super();
32
4
  this.name = 'UploadClientError';
33
5
  this.message = message;
6
+ this.code = code;
34
7
  this.request = request;
35
8
  this.response = response;
36
9
  this.headers = headers;
@@ -60,7 +33,7 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
60
33
  let aborted = false;
61
34
  xhr.open(requestMethod, url);
62
35
  if (headers) {
63
- Object.entries(headers).forEach(entry => {
36
+ Object.entries(headers).forEach((entry) => {
64
37
  const [key, value] = entry;
65
38
  typeof value !== 'undefined' &&
66
39
  !Array.isArray(value) &&
@@ -132,18 +105,25 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
132
105
  }
133
106
  });
134
107
 
108
+ function identity(obj) {
109
+ return obj;
110
+ }
111
+
112
+ const transformFile = identity;
135
113
  var getFormData = () => new FormData();
136
114
 
115
+ const isFileTuple = (tuple) => tuple[0] === 'file';
137
116
  function buildFormData(body) {
138
117
  const formData = getFormData();
139
- const isTriple = (tuple) => tuple[0] === 'file';
140
118
  for (const tuple of body) {
141
119
  if (Array.isArray(tuple[1])) {
142
120
  // refactor this
143
- tuple[1].forEach(val => val && formData.append(tuple[0] + '[]', `${val}`));
121
+ tuple[1].forEach((val) => val && formData.append(tuple[0] + '[]', `${val}`));
144
122
  }
145
- else if (isTriple(tuple)) {
146
- formData.append(tuple[0], tuple[1], tuple[2]);
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);
147
127
  }
148
128
  else if (tuple[1] != null) {
149
129
  formData.append(tuple[0], `${tuple[1]}`);
@@ -155,9 +135,9 @@ function buildFormData(body) {
155
135
  const serializePair = (key, value) => typeof value !== 'undefined' ? `${key}=${encodeURIComponent(value)}` : null;
156
136
  const createQuery = (query) => Object.entries(query)
157
137
  .reduce((params, [key, value]) => params.concat(Array.isArray(value)
158
- ? value.map(value => serializePair(`${key}[]`, value))
138
+ ? value.map((value) => serializePair(`${key}[]`, value))
159
139
  : serializePair(key, value)), [])
160
- .filter(x => !!x)
140
+ .filter((x) => !!x)
161
141
  .join('&');
162
142
  const getUrl = (base, path, query) => [
163
143
  base,
@@ -168,15 +148,52 @@ const getUrl = (base, path, query) => [
168
148
  .filter(Boolean)
169
149
  .join('');
170
150
 
171
- var version = '2.0.0-alpha.6';
151
+ /*
152
+ Settings for future support:
153
+ parallelDirectUploads: 10,
154
+ */
155
+ const defaultSettings = {
156
+ baseCDN: 'https://ucarecdn.com',
157
+ baseURL: 'https://upload.uploadcare.com',
158
+ maxContentLength: 50 * 1024 * 1024,
159
+ retryThrottledRequestMaxTimes: 1,
160
+ multipartMinFileSize: 25 * 1024 * 1024,
161
+ multipartChunkSize: 5 * 1024 * 1024,
162
+ multipartMinLastPartSize: 1024 * 1024,
163
+ maxConcurrentRequests: 4,
164
+ multipartMaxAttempts: 3,
165
+ pollingTimeoutMilliseconds: 10000,
166
+ pusherKey: '79ae88bd931ea68464d9'
167
+ };
168
+ const defaultContentType = 'application/octet-stream';
169
+ const defaultFilename = 'original';
170
+
171
+ var version = '2.0.0';
172
172
 
173
173
  /**
174
174
  * Returns User Agent based on version and settings.
175
175
  */
176
- function getUserAgent({ publicKey = '', integration = '' } = {}) {
177
- const mainInfo = [version, publicKey].filter(Boolean).join('/');
178
- const additionInfo = ['JavaScript', integration].filter(Boolean).join('; ');
179
- return `UploadcareUploadClient/${mainInfo} (${additionInfo})`;
176
+ function getUserAgent({ userAgent, publicKey = '', integration = '' } = {}) {
177
+ const libraryName = 'UploadcareUploadClient';
178
+ const libraryVersion = version;
179
+ const languageName = 'JavaScript';
180
+ if (typeof userAgent === 'string') {
181
+ return userAgent;
182
+ }
183
+ if (typeof userAgent === 'function') {
184
+ return userAgent({
185
+ publicKey,
186
+ libraryName,
187
+ libraryVersion,
188
+ languageName,
189
+ integration
190
+ });
191
+ }
192
+ const mainInfo = [libraryName, libraryVersion, publicKey]
193
+ .filter(Boolean)
194
+ .join('/');
195
+ const additionInfo = [languageName, integration].filter(Boolean).join('; ');
196
+ return `${mainInfo} (${additionInfo})`;
180
197
  }
181
198
 
182
199
  const SEPARATOR = /\W|_/g;
@@ -210,7 +227,7 @@ function camelizeKeys(source) {
210
227
  *
211
228
  * @param {number} ms Timeout in milliseconds.
212
229
  */
213
- const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
230
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
214
231
 
215
232
  const defaultOptions = {
216
233
  factor: 2,
@@ -232,7 +249,7 @@ function retrier(fn, options = defaultOptions) {
232
249
  return runAttempt(fn);
233
250
  }
234
251
 
235
- const REQUEST_WAS_THROTTLED_CODE = 429;
252
+ const REQUEST_WAS_THROTTLED_CODE = 'RequestThrottledError';
236
253
  const DEFAULT_RETRY_AFTER_TIMEOUT = 15000;
237
254
  function getTimeoutFromThrottledRequest(error) {
238
255
  const { headers } = error || {};
@@ -242,9 +259,8 @@ function getTimeoutFromThrottledRequest(error) {
242
259
  }
243
260
  function retryIfThrottled(fn, retryThrottledMaxTimes) {
244
261
  return retrier(({ attempt, retry }) => fn().catch((error) => {
245
- var _a;
246
262
  if ('response' in error &&
247
- ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.statusCode) === REQUEST_WAS_THROTTLED_CODE &&
263
+ (error === null || error === void 0 ? void 0 : error.code) === REQUEST_WAS_THROTTLED_CODE &&
248
264
  attempt < retryThrottledMaxTimes) {
249
265
  return retry(getTimeoutFromThrottledRequest(error));
250
266
  }
@@ -256,7 +272,7 @@ function retryIfThrottled(fn, retryThrottledMaxTimes) {
256
272
  * Performs file uploading request to Uploadcare Upload API.
257
273
  * Can be canceled and has progress.
258
274
  */
259
- function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
275
+ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
260
276
  return retryIfThrottled(() => {
261
277
  var _a;
262
278
  return request({
@@ -265,7 +281,7 @@ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, se
265
281
  jsonerrors: 1
266
282
  }),
267
283
  headers: {
268
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
284
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
269
285
  },
270
286
  data: buildFormData([
271
287
  ['file', file, (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename],
@@ -283,7 +299,7 @@ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, se
283
299
  }).then(({ data, headers, request }) => {
284
300
  const response = camelizeKeys(JSON.parse(data));
285
301
  if ('error' in response) {
286
- throw new UploadClientError(`[${response.error.statusCode}] ${response.error.content}`, request, response.error, headers);
302
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
287
303
  }
288
304
  else {
289
305
  return response;
@@ -292,34 +308,6 @@ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, se
292
308
  }, retryThrottledRequestMaxTimes);
293
309
  }
294
310
 
295
- /**
296
- * Returns a JSON dictionary holding file info.
297
- */
298
- /* eslint @typescript-eslint/camelcase: [2, {allow: ["pub_key", "file_id"]}] */
299
- function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, source, integration, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
300
- return retryIfThrottled(() => request({
301
- method: 'GET',
302
- headers: {
303
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
304
- },
305
- url: getUrl(baseURL, '/info/', {
306
- jsonerrors: 1,
307
- pub_key: publicKey,
308
- file_id: uuid,
309
- source
310
- }),
311
- signal
312
- }).then(({ data, headers, request }) => {
313
- const response = camelizeKeys(JSON.parse(data));
314
- if ('error' in response) {
315
- throw new UploadClientError(`[${response.error.statusCode}] ${response.error.content}`, request, response.error, headers);
316
- }
317
- else {
318
- return response;
319
- }
320
- }), retryThrottledRequestMaxTimes);
321
- }
322
-
323
311
  var TypeEnum;
324
312
  (function (TypeEnum) {
325
313
  TypeEnum["Token"] = "token";
@@ -328,12 +316,11 @@ var TypeEnum;
328
316
  /**
329
317
  * Uploading files from URL.
330
318
  */
331
- /* eslint @typescript-eslint/camelcase: [2, {allow: ["pub_key", "source_url", "check_URL_duplicates", "save_URL_duplicates"]}] */
332
- function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, store, fileName, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, source = 'url', signal, integration, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
319
+ function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, store, fileName, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, source = 'url', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
333
320
  return retryIfThrottled(() => request({
334
321
  method: 'POST',
335
322
  headers: {
336
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
323
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
337
324
  },
338
325
  url: getUrl(baseURL, '/from_url/', {
339
326
  jsonerrors: 1,
@@ -351,7 +338,7 @@ function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, stor
351
338
  }).then(({ data, headers, request }) => {
352
339
  const response = camelizeKeys(JSON.parse(data));
353
340
  if ('error' in response) {
354
- throw new UploadClientError(`[${response.error.statusCode}] ${response.error.content}`, request, response.error, headers);
341
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
355
342
  }
356
343
  else {
357
344
  return response;
@@ -373,11 +360,17 @@ const isErrorResponse = (response) => {
373
360
  /**
374
361
  * Checking upload status and working with file tokens.
375
362
  */
376
- function fromUrlStatus(token, { publicKey, baseURL = defaultSettings.baseURL, signal, integration, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes } = {}) {
363
+ function fromUrlStatus(token, { publicKey, baseURL = defaultSettings.baseURL, signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes } = {}) {
377
364
  return retryIfThrottled(() => request({
378
365
  method: 'GET',
379
366
  headers: publicKey
380
- ? { 'X-UC-User-Agent': getUserAgent({ publicKey, integration }) }
367
+ ? {
368
+ 'X-UC-User-Agent': getUserAgent({
369
+ publicKey,
370
+ integration,
371
+ userAgent
372
+ })
373
+ }
381
374
  : undefined,
382
375
  url: getUrl(baseURL, '/from_url/status/', {
383
376
  jsonerrors: 1,
@@ -387,7 +380,7 @@ function fromUrlStatus(token, { publicKey, baseURL = defaultSettings.baseURL, si
387
380
  }).then(({ data, headers, request }) => {
388
381
  const response = camelizeKeys(JSON.parse(data));
389
382
  if ('error' in response && !isErrorResponse(response)) {
390
- throw new UploadClientError(`[${response.error.statusCode}] ${response.error.content}`, request, response.error, headers);
383
+ throw new UploadClientError(response.error.content, undefined, request, response, headers);
391
384
  }
392
385
  else {
393
386
  return response;
@@ -398,12 +391,11 @@ function fromUrlStatus(token, { publicKey, baseURL = defaultSettings.baseURL, si
398
391
  /**
399
392
  * Create files group.
400
393
  */
401
- /* eslint @typescript-eslint/camelcase: [2, {allow: ["pub_key"]}] */
402
- function group(uuids, { publicKey, baseURL = defaultSettings.baseURL, jsonpCallback, secureSignature, secureExpire, signal, source, integration, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
394
+ function group(uuids, { publicKey, baseURL = defaultSettings.baseURL, jsonpCallback, secureSignature, secureExpire, signal, source, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
403
395
  return retryIfThrottled(() => request({
404
396
  method: 'POST',
405
397
  headers: {
406
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
398
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
407
399
  },
408
400
  url: getUrl(baseURL, '/group/', {
409
401
  jsonerrors: 1,
@@ -418,7 +410,7 @@ function group(uuids, { publicKey, baseURL = defaultSettings.baseURL, jsonpCallb
418
410
  }).then(({ data, headers, request }) => {
419
411
  const response = camelizeKeys(JSON.parse(data));
420
412
  if ('error' in response) {
421
- throw new UploadClientError(`[${response.error.statusCode}] ${response.error.content}`, request, response.error, headers);
413
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
422
414
  }
423
415
  else {
424
416
  return response;
@@ -429,12 +421,11 @@ function group(uuids, { publicKey, baseURL = defaultSettings.baseURL, jsonpCallb
429
421
  /**
430
422
  * Get info about group.
431
423
  */
432
- /* eslint @typescript-eslint/camelcase: [2, {allow: ["pub_key", "group_id"]}] */
433
- function groupInfo(id, { publicKey, baseURL = defaultSettings.baseURL, signal, source, integration, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
424
+ function groupInfo(id, { publicKey, baseURL = defaultSettings.baseURL, signal, source, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
434
425
  return retryIfThrottled(() => request({
435
426
  method: 'GET',
436
427
  headers: {
437
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
428
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
438
429
  },
439
430
  url: getUrl(baseURL, '/group/info/', {
440
431
  jsonerrors: 1,
@@ -446,7 +437,7 @@ function groupInfo(id, { publicKey, baseURL = defaultSettings.baseURL, signal, s
446
437
  }).then(({ data, headers, request }) => {
447
438
  const response = camelizeKeys(JSON.parse(data));
448
439
  if ('error' in response) {
449
- throw new UploadClientError(`[${response.error.statusCode}] ${response.error.content}`, request, response.error, headers);
440
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
450
441
  }
451
442
  else {
452
443
  return response;
@@ -455,62 +446,62 @@ function groupInfo(id, { publicKey, baseURL = defaultSettings.baseURL, signal, s
455
446
  }
456
447
 
457
448
  /**
458
- * Start multipart uploading.
449
+ * Returns a JSON dictionary holding file info.
459
450
  */
460
- function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', integration, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
451
+ function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, source, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
461
452
  return retryIfThrottled(() => request({
462
- method: 'POST',
463
- url: getUrl(baseURL, '/multipart/start/', { jsonerrors: 1 }),
453
+ method: 'GET',
464
454
  headers: {
465
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
455
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
466
456
  },
467
- data: buildFormData([
468
- ['filename', fileName !== null && fileName !== void 0 ? fileName : defaultFilename],
469
- ['size', size],
470
- ['content_type', contentType !== null && contentType !== void 0 ? contentType : defaultContentType],
471
- ['part_size', multipartChunkSize],
472
- ['UPLOADCARE_STORE', store ? '' : 'auto'],
473
- ['UPLOADCARE_PUB_KEY', publicKey],
474
- ['signature', secureSignature],
475
- ['expire', secureExpire],
476
- ['source', source]
477
- ]),
457
+ url: getUrl(baseURL, '/info/', {
458
+ jsonerrors: 1,
459
+ pub_key: publicKey,
460
+ file_id: uuid,
461
+ source
462
+ }),
478
463
  signal
479
464
  }).then(({ data, headers, request }) => {
480
465
  const response = camelizeKeys(JSON.parse(data));
481
466
  if ('error' in response) {
482
- throw new UploadClientError(`[${response.error.statusCode}] ${response.error.content}`, request, response.error, headers);
467
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
483
468
  }
484
469
  else {
485
- // convert to array
486
- response.parts = Object.keys(response.parts).map(key => response.parts[key]);
487
470
  return response;
488
471
  }
489
472
  }), retryThrottledRequestMaxTimes);
490
473
  }
491
474
 
492
475
  /**
493
- * Complete multipart uploading.
476
+ * Start multipart uploading.
494
477
  */
495
- function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL, source = 'local', signal, integration, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
478
+ function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
496
479
  return retryIfThrottled(() => request({
497
480
  method: 'POST',
498
- url: getUrl(baseURL, '/multipart/complete/', { jsonerrors: 1 }),
481
+ url: getUrl(baseURL, '/multipart/start/', { jsonerrors: 1 }),
499
482
  headers: {
500
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
483
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
501
484
  },
502
485
  data: buildFormData([
503
- ['uuid', uuid],
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'],
504
491
  ['UPLOADCARE_PUB_KEY', publicKey],
492
+ ['signature', secureSignature],
493
+ ['expire', secureExpire],
505
494
  ['source', source]
506
495
  ]),
507
496
  signal
508
497
  }).then(({ data, headers, request }) => {
509
498
  const response = camelizeKeys(JSON.parse(data));
510
499
  if ('error' in response) {
511
- throw new UploadClientError(`[${response.error.statusCode}] ${response.error.content}`, request, response.error, headers);
500
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
512
501
  }
513
502
  else {
503
+ // convert to array
504
+ response.parts = Object.keys(response.parts).map((key) => response.parts[key]);
514
505
  return response;
515
506
  }
516
507
  }), retryThrottledRequestMaxTimes);
@@ -519,20 +510,15 @@ function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL,
519
510
  /**
520
511
  * Complete multipart uploading.
521
512
  */
522
- function multipartUpload(part, url, { publicKey, signal, onProgress, integration }) {
513
+ function multipartUpload(part, url, { signal, onProgress }) {
523
514
  return request({
524
515
  method: 'PUT',
525
516
  url,
526
- headers: {
527
- 'X-UC-User-Agent': publicKey
528
- ? getUserAgent({ publicKey, integration })
529
- : undefined
530
- },
531
517
  data: part,
532
518
  onProgress,
533
519
  signal
534
520
  })
535
- .then(result => {
521
+ .then((result) => {
536
522
  // hack for node ¯\_(ツ)_/¯
537
523
  if (onProgress)
538
524
  onProgress({ value: 1 });
@@ -541,12 +527,40 @@ function multipartUpload(part, url, { publicKey, signal, onProgress, integration
541
527
  .then(({ status }) => ({ code: status }));
542
528
  }
543
529
 
530
+ /**
531
+ * Complete multipart uploading.
532
+ */
533
+ function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL, source = 'local', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
534
+ return retryIfThrottled(() => request({
535
+ method: 'POST',
536
+ url: getUrl(baseURL, '/multipart/complete/', { jsonerrors: 1 }),
537
+ headers: {
538
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
539
+ },
540
+ data: buildFormData([
541
+ ['uuid', uuid],
542
+ ['UPLOADCARE_PUB_KEY', publicKey],
543
+ ['source', source]
544
+ ]),
545
+ signal
546
+ }).then(({ data, headers, request }) => {
547
+ const response = camelizeKeys(JSON.parse(data));
548
+ if ('error' in response) {
549
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
550
+ }
551
+ else {
552
+ return response;
553
+ }
554
+ }), retryThrottledRequestMaxTimes);
555
+ }
556
+
544
557
  class UploadcareFile {
545
558
  constructor(fileInfo, { baseCDN, defaultEffects, fileName }) {
546
559
  this.name = null;
547
560
  this.size = null;
548
561
  this.isStored = null;
549
562
  this.isImage = null;
563
+ this.mimeType = null;
550
564
  this.cdnUrl = null;
551
565
  this.cdnUrlModifiers = null;
552
566
  this.originalUrl = null;
@@ -565,6 +579,7 @@ class UploadcareFile {
565
579
  this.size = fileInfo.size;
566
580
  this.isStored = fileInfo.isStored;
567
581
  this.isImage = fileInfo.isImage;
582
+ this.mimeType = fileInfo.mimeType;
568
583
  this.cdnUrl = cdnUrl;
569
584
  this.cdnUrlModifiers = cdnUrlModifiers;
570
585
  this.originalUrl = originalUrl;
@@ -584,7 +599,7 @@ const poll = ({ check, interval = DEFAULT_INTERVAL, signal }) => new Promise((re
584
599
  const tick = () => {
585
600
  try {
586
601
  Promise.resolve(check(signal))
587
- .then(result => {
602
+ .then((result) => {
588
603
  if (result) {
589
604
  resolve(result);
590
605
  }
@@ -592,7 +607,7 @@ const poll = ({ check, interval = DEFAULT_INTERVAL, signal }) => new Promise((re
592
607
  timeoutId = setTimeout(tick, interval);
593
608
  }
594
609
  })
595
- .catch(error => reject(error));
610
+ .catch((error) => reject(error));
596
611
  }
597
612
  catch (error) {
598
613
  reject(error);
@@ -601,16 +616,17 @@ const poll = ({ check, interval = DEFAULT_INTERVAL, signal }) => new Promise((re
601
616
  timeoutId = setTimeout(tick, 0);
602
617
  });
603
618
 
604
- function isReadyPoll({ file, publicKey, baseURL, source, integration, retryThrottledRequestMaxTimes, signal, onProgress }) {
619
+ function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent, retryThrottledRequestMaxTimes, signal, onProgress }) {
605
620
  return poll({
606
- check: signal => info(file, {
621
+ check: (signal) => info(file, {
607
622
  publicKey,
608
623
  baseURL,
609
624
  signal,
610
625
  source,
611
626
  integration,
627
+ userAgent,
612
628
  retryThrottledRequestMaxTimes
613
- }).then(response => {
629
+ }).then((response) => {
614
630
  if (response.isReady) {
615
631
  return response;
616
632
  }
@@ -621,7 +637,7 @@ function isReadyPoll({ file, publicKey, baseURL, source, integration, retryThrot
621
637
  });
622
638
  }
623
639
 
624
- const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, retryThrottledRequestMaxTimes, baseCDN }) => {
640
+ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN }) => {
625
641
  return base(file, {
626
642
  publicKey,
627
643
  fileName,
@@ -633,6 +649,7 @@ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature,
633
649
  onProgress,
634
650
  source,
635
651
  integration,
652
+ userAgent,
636
653
  retryThrottledRequestMaxTimes
637
654
  })
638
655
  .then(({ file }) => {
@@ -642,14 +659,23 @@ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature,
642
659
  baseURL,
643
660
  source,
644
661
  integration,
662
+ userAgent,
645
663
  retryThrottledRequestMaxTimes,
646
664
  onProgress,
647
665
  signal
648
666
  });
649
667
  })
650
- .then(fileInfo => new UploadcareFile(fileInfo, { baseCDN }));
668
+ .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
651
669
  };
652
670
 
671
+ /*globals self, window */
672
+
673
+ /*eslint-disable @mysticatea/prettier */
674
+ const { AbortController, AbortSignal } =
675
+ typeof self !== "undefined" ? self :
676
+ typeof window !== "undefined" ? window :
677
+ /* otherwise */ undefined;
678
+
653
679
  const race = (fns, { signal } = {}) => {
654
680
  let lastError = null;
655
681
  let winnerIndex = null;
@@ -659,21 +685,21 @@ const race = (fns, { signal } = {}) => {
659
685
  controllers.forEach((controller, index) => index !== i && controller.abort());
660
686
  };
661
687
  onCancel(signal, () => {
662
- controllers.forEach(controller => controller.abort());
688
+ controllers.forEach((controller) => controller.abort());
663
689
  });
664
690
  return Promise.all(fns.map((fn, i) => {
665
691
  const stopRace = createStopRaceCallback(i);
666
692
  return Promise.resolve()
667
693
  .then(() => fn({ stopRace, signal: controllers[i].signal }))
668
- .then(result => {
694
+ .then((result) => {
669
695
  stopRace();
670
696
  return result;
671
697
  })
672
- .catch(error => {
698
+ .catch((error) => {
673
699
  lastError = error;
674
700
  return null;
675
701
  });
676
- })).then(results => {
702
+ })).then((results) => {
677
703
  if (winnerIndex === null) {
678
704
  throw lastError;
679
705
  }
@@ -691,7 +717,7 @@ class Events {
691
717
  }
692
718
  emit(event, data) {
693
719
  var _a;
694
- (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.forEach(fn => fn(data));
720
+ (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.forEach((fn) => fn(data));
695
721
  }
696
722
  on(event, callback) {
697
723
  this.events[event] = this.events[event] || [];
@@ -699,7 +725,7 @@ class Events {
699
725
  }
700
726
  off(event, callback) {
701
727
  if (callback) {
702
- this.events[event] = this.events[event].filter(fn => fn !== callback);
728
+ this.events[event] = this.events[event].filter((fn) => fn !== callback);
703
729
  }
704
730
  else {
705
731
  this.events[event] = [];
@@ -732,16 +758,16 @@ class Pusher {
732
758
  if (!this.isConnected && !this.ws) {
733
759
  const pusherUrl = `wss://ws.pusherapp.com/app/${this.key}?protocol=5&client=js&version=1.12.2`;
734
760
  this.ws = new WebSocket(pusherUrl);
735
- this.ws.addEventListener('error', error => {
761
+ this.ws.addEventListener('error', (error) => {
736
762
  this.emmitter.emit('error', new Error(error.message));
737
763
  });
738
764
  this.emmitter.on('connected', () => {
739
765
  this.isConnected = true;
740
- this.queue.forEach(message => this.send(message.event, message.data));
766
+ this.queue.forEach((message) => this.send(message.event, message.data));
741
767
  this.queue = [];
742
768
  });
743
- this.ws.addEventListener('message', e => {
744
- const data = JSON.parse(e.data);
769
+ this.ws.addEventListener('message', (e) => {
770
+ const data = JSON.parse(e.data.toString());
745
771
  switch (data.event) {
746
772
  case 'pusher:connection_established': {
747
773
  this.emmitter.emit('connected', undefined);
@@ -809,7 +835,7 @@ class Pusher {
809
835
  this.send(message.event, message.data);
810
836
  }
811
837
  else {
812
- this.queue = this.queue.filter(msg => msg.data.channel !== channel);
838
+ this.queue = this.queue.filter((msg) => msg.data.channel !== channel);
813
839
  }
814
840
  if (this.subscribers === 0) {
815
841
  this.disconnect();
@@ -833,18 +859,19 @@ const preconnect = (key) => {
833
859
  getPusher(key).connect();
834
860
  };
835
861
 
836
- function pollStrategy({ token, publicKey, baseURL, integration, retryThrottledRequestMaxTimes, onProgress, signal }) {
862
+ function pollStrategy({ token, publicKey, baseURL, integration, userAgent, retryThrottledRequestMaxTimes, onProgress, signal }) {
837
863
  return poll({
838
- check: signal => fromUrlStatus(token, {
864
+ check: (signal) => fromUrlStatus(token, {
839
865
  publicKey,
840
866
  baseURL,
841
867
  integration,
868
+ userAgent,
842
869
  retryThrottledRequestMaxTimes,
843
870
  signal
844
- }).then(response => {
871
+ }).then((response) => {
845
872
  switch (response.status) {
846
873
  case Status.Error: {
847
- return new UploadClientError(response.error);
874
+ return new UploadClientError(response.error, response.errorCode);
848
875
  }
849
876
  case Status.Waiting: {
850
877
  return false;
@@ -870,7 +897,7 @@ function pollStrategy({ token, publicKey, baseURL, integration, retryThrottledRe
870
897
  signal
871
898
  });
872
899
  }
873
- const pushStrategy = ({ token, pusherKey, signal, stopRace, onProgress }) => new Promise((resolve, reject) => {
900
+ const pushStrategy = ({ token, pusherKey, signal, onProgress }) => new Promise((resolve, reject) => {
874
901
  const pusher = getPusher(pusherKey);
875
902
  const unsubErrorHandler = pusher.onError(reject);
876
903
  const destroy = () => {
@@ -879,10 +906,9 @@ const pushStrategy = ({ token, pusherKey, signal, stopRace, onProgress }) => new
879
906
  };
880
907
  onCancel(signal, () => {
881
908
  destroy();
882
- reject(cancelError('pisher cancelled'));
909
+ reject(cancelError('pusher cancelled'));
883
910
  });
884
- pusher.subscribe(token, result => {
885
- stopRace();
911
+ pusher.subscribe(token, (result) => {
886
912
  switch (result.status) {
887
913
  case Status.Progress: {
888
914
  if (onProgress) {
@@ -899,12 +925,12 @@ const pushStrategy = ({ token, pusherKey, signal, stopRace, onProgress }) => new
899
925
  }
900
926
  case Status.Error: {
901
927
  destroy();
902
- reject(new UploadClientError(result.msg));
928
+ reject(new UploadClientError(result.msg, result.error_code));
903
929
  }
904
930
  }
905
931
  });
906
932
  });
907
- const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, store, signal, onProgress, source, integration, retryThrottledRequestMaxTimes, pusherKey = defaultSettings.pusherKey }) => Promise.resolve(preconnect(pusherKey))
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))
908
934
  .then(() => fromUrl(sourceUrl, {
909
935
  publicKey,
910
936
  fileName,
@@ -917,9 +943,15 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
917
943
  signal,
918
944
  source,
919
945
  integration,
946
+ userAgent,
920
947
  retryThrottledRequestMaxTimes
921
948
  }))
922
- .then(urlResponse => {
949
+ .catch((error) => {
950
+ const pusher = getPusher(pusherKey);
951
+ pusher === null || pusher === void 0 ? void 0 : pusher.disconnect();
952
+ return Promise.reject(error);
953
+ })
954
+ .then((urlResponse) => {
923
955
  if (urlResponse.type === TypeEnum.FileInfo) {
924
956
  return urlResponse;
925
957
  }
@@ -930,47 +962,49 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
930
962
  publicKey,
931
963
  baseURL,
932
964
  integration,
965
+ userAgent,
933
966
  retryThrottledRequestMaxTimes,
934
967
  onProgress,
935
968
  signal
936
969
  }),
937
- ({ stopRace, signal }) => pushStrategy({
970
+ ({ signal }) => pushStrategy({
938
971
  token: urlResponse.token,
939
972
  pusherKey,
940
- stopRace,
941
973
  signal,
942
974
  onProgress
943
975
  })
944
976
  ], { signal });
945
977
  }
946
978
  })
947
- .then(result => {
979
+ .then((result) => {
948
980
  if (result instanceof UploadClientError)
949
981
  throw result;
950
982
  return result;
951
983
  })
952
- .then(result => isReadyPoll({
984
+ .then((result) => isReadyPoll({
953
985
  file: result.uuid,
954
986
  publicKey,
955
987
  baseURL,
956
988
  integration,
989
+ userAgent,
957
990
  retryThrottledRequestMaxTimes,
958
991
  onProgress,
959
992
  signal
960
993
  }))
961
- .then(fileInfo => new UploadcareFile(fileInfo, { baseCDN }));
994
+ .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
962
995
 
963
- const uploadFromUploaded = (uuid, { publicKey, fileName, baseURL, signal, onProgress, source, integration, retryThrottledRequestMaxTimes, baseCDN }) => {
996
+ const uploadFromUploaded = (uuid, { publicKey, fileName, baseURL, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN }) => {
964
997
  return info(uuid, {
965
998
  publicKey,
966
999
  baseURL,
967
1000
  signal,
968
1001
  source,
969
1002
  integration,
1003
+ userAgent,
970
1004
  retryThrottledRequestMaxTimes
971
1005
  })
972
- .then(fileInfo => new UploadcareFile(fileInfo, { baseCDN, fileName }))
973
- .then(result => {
1006
+ .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN, fileName }))
1007
+ .then((result) => {
974
1008
  // hack for node ¯\_(ツ)_/¯
975
1009
  if (onProgress)
976
1010
  onProgress({ value: 1 });
@@ -1019,6 +1053,16 @@ const isMultipart = (fileSize, multipartMinFileSize = defaultSettings.multipartM
1019
1053
  return fileSize >= multipartMinFileSize;
1020
1054
  };
1021
1055
 
1056
+ const sliceChunk = (file, index, fileSize, chunkSize) => {
1057
+ const start = chunkSize * index;
1058
+ const end = Math.min(start + chunkSize, fileSize);
1059
+ return file.slice(start, end);
1060
+ };
1061
+
1062
+ function prepareChunks(file, fileSize, chunkSize) {
1063
+ return (index) => sliceChunk(file, index, fileSize, chunkSize);
1064
+ }
1065
+
1022
1066
  const runWithConcurrency = (concurrency, tasks) => {
1023
1067
  return new Promise((resolve, reject) => {
1024
1068
  const results = [];
@@ -1042,7 +1086,7 @@ const runWithConcurrency = (concurrency, tasks) => {
1042
1086
  resolve(results);
1043
1087
  }
1044
1088
  })
1045
- .catch(error => {
1089
+ .catch((error) => {
1046
1090
  rejected = true;
1047
1091
  reject(error);
1048
1092
  });
@@ -1054,23 +1098,18 @@ const runWithConcurrency = (concurrency, tasks) => {
1054
1098
  });
1055
1099
  };
1056
1100
 
1057
- const getChunk = (file, index, fileSize, chunkSize) => {
1058
- const start = chunkSize * index;
1059
- const end = Math.min(start + chunkSize, fileSize);
1060
- return file.slice(start, end);
1061
- };
1062
1101
  const uploadPartWithRetry = (chunk, url, { publicKey, onProgress, signal, integration, multipartMaxAttempts }) => retrier(({ attempt, retry }) => multipartUpload(chunk, url, {
1063
1102
  publicKey,
1064
1103
  onProgress,
1065
1104
  signal,
1066
1105
  integration
1067
- }).catch(error => {
1106
+ }).catch((error) => {
1068
1107
  if (attempt < multipartMaxAttempts) {
1069
1108
  return retry();
1070
1109
  }
1071
1110
  throw error;
1072
1111
  }));
1073
- const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, retryThrottledRequestMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, maxConcurrentRequests = defaultSettings.maxConcurrentRequests, multipartMaxAttempts = defaultSettings.multipartMaxAttempts, baseCDN }) => {
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 }) => {
1074
1113
  const size = fileSize || getFileSize(file);
1075
1114
  let progressValues;
1076
1115
  const createProgressHandler = (size, index) => {
@@ -1096,26 +1135,31 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1096
1135
  signal,
1097
1136
  source,
1098
1137
  integration,
1138
+ userAgent,
1099
1139
  retryThrottledRequestMaxTimes
1100
1140
  })
1101
- .then(({ uuid, parts }) => Promise.all([
1102
- uuid,
1103
- runWithConcurrency(maxConcurrentRequests, parts.map((url, index) => () => uploadPartWithRetry(getChunk(file, index, size, multipartChunkSize), url, {
1104
- publicKey,
1105
- onProgress: createProgressHandler(parts.length, index),
1106
- signal,
1107
- integration,
1108
- multipartMaxAttempts
1109
- })))
1110
- ]))
1141
+ .then(({ uuid, parts }) => {
1142
+ const getChunk = prepareChunks(file, size, multipartChunkSize);
1143
+ return Promise.all([
1144
+ uuid,
1145
+ runWithConcurrency(maxConcurrentRequests, parts.map((url, index) => () => uploadPartWithRetry(getChunk(index), url, {
1146
+ publicKey,
1147
+ onProgress: createProgressHandler(parts.length, index),
1148
+ signal,
1149
+ integration,
1150
+ multipartMaxAttempts
1151
+ })))
1152
+ ]);
1153
+ })
1111
1154
  .then(([uuid]) => multipartComplete(uuid, {
1112
1155
  publicKey,
1113
1156
  baseURL,
1114
1157
  source,
1115
1158
  integration,
1159
+ userAgent,
1116
1160
  retryThrottledRequestMaxTimes
1117
1161
  }))
1118
- .then(fileInfo => {
1162
+ .then((fileInfo) => {
1119
1163
  if (fileInfo.isReady) {
1120
1164
  return fileInfo;
1121
1165
  }
@@ -1126,19 +1170,20 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1126
1170
  baseURL,
1127
1171
  source,
1128
1172
  integration,
1173
+ userAgent,
1129
1174
  retryThrottledRequestMaxTimes,
1130
1175
  onProgress,
1131
1176
  signal
1132
1177
  });
1133
1178
  }
1134
1179
  })
1135
- .then(fileInfo => new UploadcareFile(fileInfo, { baseCDN }));
1180
+ .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
1136
1181
  };
1137
1182
 
1138
1183
  /**
1139
1184
  * Uploads file from provided data.
1140
1185
  */
1141
- function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, retryThrottledRequestMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, baseCDN = defaultSettings.baseCDN }) {
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 }) {
1142
1187
  if (isFileData(data)) {
1143
1188
  const fileSize = getFileSize(data);
1144
1189
  if (isMultipart(fileSize)) {
@@ -1146,6 +1191,7 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1146
1191
  publicKey,
1147
1192
  contentType,
1148
1193
  multipartChunkSize,
1194
+ multipartMaxAttempts,
1149
1195
  fileName,
1150
1196
  baseURL,
1151
1197
  secureSignature,
@@ -1155,6 +1201,8 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1155
1201
  onProgress,
1156
1202
  source,
1157
1203
  integration,
1204
+ userAgent,
1205
+ maxConcurrentRequests,
1158
1206
  retryThrottledRequestMaxTimes,
1159
1207
  baseCDN
1160
1208
  });
@@ -1170,6 +1218,7 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1170
1218
  onProgress,
1171
1219
  source,
1172
1220
  integration,
1221
+ userAgent,
1173
1222
  retryThrottledRequestMaxTimes,
1174
1223
  baseCDN
1175
1224
  });
@@ -1179,6 +1228,9 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1179
1228
  publicKey,
1180
1229
  fileName,
1181
1230
  baseURL,
1231
+ baseCDN,
1232
+ checkForUrlDuplicates,
1233
+ saveUrlForRecurrentUploads,
1182
1234
  secureSignature,
1183
1235
  secureExpire,
1184
1236
  store,
@@ -1186,8 +1238,9 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1186
1238
  onProgress,
1187
1239
  source,
1188
1240
  integration,
1241
+ userAgent,
1189
1242
  retryThrottledRequestMaxTimes,
1190
- baseCDN
1243
+ pusherKey
1191
1244
  });
1192
1245
  }
1193
1246
  if (isUuid(data)) {
@@ -1199,6 +1252,7 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1199
1252
  onProgress,
1200
1253
  source,
1201
1254
  integration,
1255
+ userAgent,
1202
1256
  retryThrottledRequestMaxTimes,
1203
1257
  baseCDN
1204
1258
  });
@@ -1213,8 +1267,7 @@ class UploadcareGroup {
1213
1267
  this.filesCount = groupInfo.filesCount;
1214
1268
  this.totalSize = Object.values(groupInfo.files).reduce((acc, file) => acc + file.size, 0);
1215
1269
  this.isStored = !!groupInfo.datetimeStored;
1216
- this.isImage = !!Object.values(groupInfo.files).filter(file => file.isImage)
1217
- .length;
1270
+ this.isImage = !!Object.values(groupInfo.files).filter((file) => file.isImage).length;
1218
1271
  this.cdnUrl = groupInfo.cdnUrl;
1219
1272
  this.files = files;
1220
1273
  this.createdAt = groupInfo.datetimeCreated;
@@ -1256,7 +1309,7 @@ const isUrlArray = (data) => {
1256
1309
  return true;
1257
1310
  };
1258
1311
 
1259
- function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, retryThrottledRequestMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, baseCDN = defaultSettings.baseCDN, jsonpCallback, defaultEffects }) {
1312
+ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, baseCDN = defaultSettings.baseCDN, jsonpCallback, defaultEffects }) {
1260
1313
  if (!isFileDataArray(data) && !isUrlArray(data) && !isUuidArray(data)) {
1261
1314
  throw new TypeError(`Group uploading from "${data}" is not supported`);
1262
1315
  }
@@ -1285,12 +1338,13 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1285
1338
  onProgress: createProgressHandler(filesCount, index),
1286
1339
  source,
1287
1340
  integration,
1341
+ userAgent,
1288
1342
  retryThrottledRequestMaxTimes,
1289
1343
  contentType,
1290
1344
  multipartChunkSize,
1291
1345
  baseCDN
1292
- }))).then(files => {
1293
- const uuids = files.map(file => file.uuid);
1346
+ }))).then((files) => {
1347
+ const uuids = files.map((file) => file.uuid);
1294
1348
  const addDefaultEffects = (file) => {
1295
1349
  const cdnUrlModifiers = defaultEffects ? `-/${defaultEffects}` : null;
1296
1350
  const cdnUrl = `${file.urlBase}${cdnUrlModifiers || ''}`;
@@ -1307,8 +1361,9 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1307
1361
  signal,
1308
1362
  source,
1309
1363
  integration,
1364
+ userAgent,
1310
1365
  retryThrottledRequestMaxTimes
1311
- }).then(groupInfo => new UploadcareGroup(groupInfo, filesInGroup));
1366
+ }).then((groupInfo) => new UploadcareGroup(groupInfo, filesInGroup));
1312
1367
  });
1313
1368
  }
1314
1369
 
@@ -1372,4 +1427,4 @@ class UploadClient {
1372
1427
  }
1373
1428
  }
1374
1429
 
1375
- export { AbortController, UploadClient, base, fromUrl, fromUrlStatus, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadFile, uploadFileGroup };
1430
+ export { AbortController, UploadClient, base, fromUrl, fromUrlStatus, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadFromObject as uploadBase, uploadFile, uploadFileGroup, uploadFromUploaded, uploadFromUrl, uploadMultipart };