@uploadcare/upload-client 2.0.0-alpha.7 → 2.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.
@@ -1,8 +1,9 @@
1
1
  class UploadClientError extends Error {
2
- constructor(message, request, response, headers) {
2
+ constructor(message, code, request, response, headers) {
3
3
  super();
4
4
  this.name = 'UploadClientError';
5
5
  this.message = message;
6
+ this.code = code;
6
7
  this.request = request;
7
8
  this.response = response;
8
9
  this.headers = headers;
@@ -32,7 +33,7 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
32
33
  let aborted = false;
33
34
  xhr.open(requestMethod, url);
34
35
  if (headers) {
35
- Object.entries(headers).forEach(entry => {
36
+ Object.entries(headers).forEach((entry) => {
36
37
  const [key, value] = entry;
37
38
  typeof value !== 'undefined' &&
38
39
  !Array.isArray(value) &&
@@ -104,18 +105,25 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
104
105
  }
105
106
  });
106
107
 
108
+ function identity(obj) {
109
+ return obj;
110
+ }
111
+
112
+ const transformFile = identity;
107
113
  var getFormData = () => new FormData();
108
114
 
115
+ const isFileTuple = (tuple) => tuple[0] === 'file';
109
116
  function buildFormData(body) {
110
117
  const formData = getFormData();
111
- const isTriple = (tuple) => tuple[0] === 'file';
112
118
  for (const tuple of body) {
113
119
  if (Array.isArray(tuple[1])) {
114
120
  // refactor this
115
- tuple[1].forEach(val => val && formData.append(tuple[0] + '[]', `${val}`));
121
+ tuple[1].forEach((val) => val && formData.append(tuple[0] + '[]', `${val}`));
116
122
  }
117
- else if (isTriple(tuple)) {
118
- 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);
119
127
  }
120
128
  else if (tuple[1] != null) {
121
129
  formData.append(tuple[0], `${tuple[1]}`);
@@ -127,9 +135,9 @@ function buildFormData(body) {
127
135
  const serializePair = (key, value) => typeof value !== 'undefined' ? `${key}=${encodeURIComponent(value)}` : null;
128
136
  const createQuery = (query) => Object.entries(query)
129
137
  .reduce((params, [key, value]) => params.concat(Array.isArray(value)
130
- ? value.map(value => serializePair(`${key}[]`, value))
138
+ ? value.map((value) => serializePair(`${key}[]`, value))
131
139
  : serializePair(key, value)), [])
132
- .filter(x => !!x)
140
+ .filter((x) => !!x)
133
141
  .join('&');
134
142
  const getUrl = (base, path, query) => [
135
143
  base,
@@ -160,15 +168,32 @@ const defaultSettings = {
160
168
  const defaultContentType = 'application/octet-stream';
161
169
  const defaultFilename = 'original';
162
170
 
163
- var version = '1.1.2';
171
+ var version = '2.1.0';
164
172
 
165
173
  /**
166
174
  * Returns User Agent based on version and settings.
167
175
  */
168
- function getUserAgent({ publicKey = '', integration = '' } = {}) {
169
- const mainInfo = [version, publicKey].filter(Boolean).join('/');
170
- const additionInfo = ['JavaScript', integration].filter(Boolean).join('; ');
171
- 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})`;
172
197
  }
173
198
 
174
199
  const SEPARATOR = /\W|_/g;
@@ -202,7 +227,7 @@ function camelizeKeys(source) {
202
227
  *
203
228
  * @param {number} ms Timeout in milliseconds.
204
229
  */
205
- const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
230
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
206
231
 
207
232
  const defaultOptions = {
208
233
  factor: 2,
@@ -224,7 +249,7 @@ function retrier(fn, options = defaultOptions) {
224
249
  return runAttempt(fn);
225
250
  }
226
251
 
227
- const REQUEST_WAS_THROTTLED_CODE = 429;
252
+ const REQUEST_WAS_THROTTLED_CODE = 'RequestThrottledError';
228
253
  const DEFAULT_RETRY_AFTER_TIMEOUT = 15000;
229
254
  function getTimeoutFromThrottledRequest(error) {
230
255
  const { headers } = error || {};
@@ -234,9 +259,8 @@ function getTimeoutFromThrottledRequest(error) {
234
259
  }
235
260
  function retryIfThrottled(fn, retryThrottledMaxTimes) {
236
261
  return retrier(({ attempt, retry }) => fn().catch((error) => {
237
- var _a;
238
262
  if ('response' in error &&
239
- ((_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 &&
240
264
  attempt < retryThrottledMaxTimes) {
241
265
  return retry(getTimeoutFromThrottledRequest(error));
242
266
  }
@@ -248,7 +272,7 @@ function retryIfThrottled(fn, retryThrottledMaxTimes) {
248
272
  * Performs file uploading request to Uploadcare Upload API.
249
273
  * Can be canceled and has progress.
250
274
  */
251
- 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 }) {
252
276
  return retryIfThrottled(() => {
253
277
  var _a;
254
278
  return request({
@@ -257,7 +281,7 @@ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, se
257
281
  jsonerrors: 1
258
282
  }),
259
283
  headers: {
260
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
284
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
261
285
  },
262
286
  data: buildFormData([
263
287
  ['file', file, (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename],
@@ -275,7 +299,7 @@ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, se
275
299
  }).then(({ data, headers, request }) => {
276
300
  const response = camelizeKeys(JSON.parse(data));
277
301
  if ('error' in response) {
278
- 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);
279
303
  }
280
304
  else {
281
305
  return response;
@@ -292,12 +316,11 @@ var TypeEnum;
292
316
  /**
293
317
  * Uploading files from URL.
294
318
  */
295
- /* eslint @typescript-eslint/camelcase: [2, {allow: ["pub_key", "source_url", "check_URL_duplicates", "save_URL_duplicates"]}] */
296
- 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 }) {
297
320
  return retryIfThrottled(() => request({
298
321
  method: 'POST',
299
322
  headers: {
300
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
323
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
301
324
  },
302
325
  url: getUrl(baseURL, '/from_url/', {
303
326
  jsonerrors: 1,
@@ -315,7 +338,7 @@ function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, stor
315
338
  }).then(({ data, headers, request }) => {
316
339
  const response = camelizeKeys(JSON.parse(data));
317
340
  if ('error' in response) {
318
- 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);
319
342
  }
320
343
  else {
321
344
  return response;
@@ -337,11 +360,17 @@ const isErrorResponse = (response) => {
337
360
  /**
338
361
  * Checking upload status and working with file tokens.
339
362
  */
340
- 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 } = {}) {
341
364
  return retryIfThrottled(() => request({
342
365
  method: 'GET',
343
366
  headers: publicKey
344
- ? { 'X-UC-User-Agent': getUserAgent({ publicKey, integration }) }
367
+ ? {
368
+ 'X-UC-User-Agent': getUserAgent({
369
+ publicKey,
370
+ integration,
371
+ userAgent
372
+ })
373
+ }
345
374
  : undefined,
346
375
  url: getUrl(baseURL, '/from_url/status/', {
347
376
  jsonerrors: 1,
@@ -351,7 +380,7 @@ function fromUrlStatus(token, { publicKey, baseURL = defaultSettings.baseURL, si
351
380
  }).then(({ data, headers, request }) => {
352
381
  const response = camelizeKeys(JSON.parse(data));
353
382
  if ('error' in response && !isErrorResponse(response)) {
354
- throw new UploadClientError(`[${response.error.statusCode}] ${response.error.content}`, request, response.error, headers);
383
+ throw new UploadClientError(response.error.content, undefined, request, response, headers);
355
384
  }
356
385
  else {
357
386
  return response;
@@ -362,12 +391,11 @@ function fromUrlStatus(token, { publicKey, baseURL = defaultSettings.baseURL, si
362
391
  /**
363
392
  * Create files group.
364
393
  */
365
- /* eslint @typescript-eslint/camelcase: [2, {allow: ["pub_key"]}] */
366
- 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 }) {
367
395
  return retryIfThrottled(() => request({
368
396
  method: 'POST',
369
397
  headers: {
370
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
398
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
371
399
  },
372
400
  url: getUrl(baseURL, '/group/', {
373
401
  jsonerrors: 1,
@@ -382,7 +410,7 @@ function group(uuids, { publicKey, baseURL = defaultSettings.baseURL, jsonpCallb
382
410
  }).then(({ data, headers, request }) => {
383
411
  const response = camelizeKeys(JSON.parse(data));
384
412
  if ('error' in response) {
385
- 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);
386
414
  }
387
415
  else {
388
416
  return response;
@@ -393,12 +421,11 @@ function group(uuids, { publicKey, baseURL = defaultSettings.baseURL, jsonpCallb
393
421
  /**
394
422
  * Get info about group.
395
423
  */
396
- /* eslint @typescript-eslint/camelcase: [2, {allow: ["pub_key", "group_id"]}] */
397
- 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 }) {
398
425
  return retryIfThrottled(() => request({
399
426
  method: 'GET',
400
427
  headers: {
401
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
428
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
402
429
  },
403
430
  url: getUrl(baseURL, '/group/info/', {
404
431
  jsonerrors: 1,
@@ -410,7 +437,7 @@ function groupInfo(id, { publicKey, baseURL = defaultSettings.baseURL, signal, s
410
437
  }).then(({ data, headers, request }) => {
411
438
  const response = camelizeKeys(JSON.parse(data));
412
439
  if ('error' in response) {
413
- 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);
414
441
  }
415
442
  else {
416
443
  return response;
@@ -421,12 +448,11 @@ function groupInfo(id, { publicKey, baseURL = defaultSettings.baseURL, signal, s
421
448
  /**
422
449
  * Returns a JSON dictionary holding file info.
423
450
  */
424
- /* eslint @typescript-eslint/camelcase: [2, {allow: ["pub_key", "file_id"]}] */
425
- function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, source, integration, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
451
+ function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, source, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
426
452
  return retryIfThrottled(() => request({
427
453
  method: 'GET',
428
454
  headers: {
429
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
455
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
430
456
  },
431
457
  url: getUrl(baseURL, '/info/', {
432
458
  jsonerrors: 1,
@@ -438,7 +464,7 @@ function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, sour
438
464
  }).then(({ data, headers, request }) => {
439
465
  const response = camelizeKeys(JSON.parse(data));
440
466
  if ('error' in response) {
441
- 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);
442
468
  }
443
469
  else {
444
470
  return response;
@@ -449,12 +475,12 @@ function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, sour
449
475
  /**
450
476
  * Start multipart uploading.
451
477
  */
452
- function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', 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 }) {
453
479
  return retryIfThrottled(() => request({
454
480
  method: 'POST',
455
481
  url: getUrl(baseURL, '/multipart/start/', { jsonerrors: 1 }),
456
482
  headers: {
457
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
483
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
458
484
  },
459
485
  data: buildFormData([
460
486
  ['filename', fileName !== null && fileName !== void 0 ? fileName : defaultFilename],
@@ -471,11 +497,11 @@ function multipartStart(size, { publicKey, contentType, fileName, multipartChunk
471
497
  }).then(({ data, headers, request }) => {
472
498
  const response = camelizeKeys(JSON.parse(data));
473
499
  if ('error' in response) {
474
- 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);
475
501
  }
476
502
  else {
477
503
  // convert to array
478
- response.parts = Object.keys(response.parts).map(key => response.parts[key]);
504
+ response.parts = Object.keys(response.parts).map((key) => response.parts[key]);
479
505
  return response;
480
506
  }
481
507
  }), retryThrottledRequestMaxTimes);
@@ -484,20 +510,15 @@ function multipartStart(size, { publicKey, contentType, fileName, multipartChunk
484
510
  /**
485
511
  * Complete multipart uploading.
486
512
  */
487
- function multipartUpload(part, url, { publicKey, signal, onProgress, integration }) {
513
+ function multipartUpload(part, url, { signal, onProgress }) {
488
514
  return request({
489
515
  method: 'PUT',
490
516
  url,
491
- headers: {
492
- 'X-UC-User-Agent': publicKey
493
- ? getUserAgent({ publicKey, integration })
494
- : undefined
495
- },
496
517
  data: part,
497
518
  onProgress,
498
519
  signal
499
520
  })
500
- .then(result => {
521
+ .then((result) => {
501
522
  // hack for node ¯\_(ツ)_/¯
502
523
  if (onProgress)
503
524
  onProgress({ value: 1 });
@@ -509,12 +530,12 @@ function multipartUpload(part, url, { publicKey, signal, onProgress, integration
509
530
  /**
510
531
  * Complete multipart uploading.
511
532
  */
512
- function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL, source = 'local', signal, integration, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
533
+ function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL, source = 'local', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
513
534
  return retryIfThrottled(() => request({
514
535
  method: 'POST',
515
536
  url: getUrl(baseURL, '/multipart/complete/', { jsonerrors: 1 }),
516
537
  headers: {
517
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration })
538
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
518
539
  },
519
540
  data: buildFormData([
520
541
  ['uuid', uuid],
@@ -525,7 +546,7 @@ function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL,
525
546
  }).then(({ data, headers, request }) => {
526
547
  const response = camelizeKeys(JSON.parse(data));
527
548
  if ('error' in response) {
528
- throw new UploadClientError(`[${response.error.statusCode}] ${response.error.content}`, request, response.error, headers);
549
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
529
550
  }
530
551
  else {
531
552
  return response;
@@ -539,6 +560,7 @@ class UploadcareFile {
539
560
  this.size = null;
540
561
  this.isStored = null;
541
562
  this.isImage = null;
563
+ this.mimeType = null;
542
564
  this.cdnUrl = null;
543
565
  this.cdnUrlModifiers = null;
544
566
  this.originalUrl = null;
@@ -557,6 +579,7 @@ class UploadcareFile {
557
579
  this.size = fileInfo.size;
558
580
  this.isStored = fileInfo.isStored;
559
581
  this.isImage = fileInfo.isImage;
582
+ this.mimeType = fileInfo.mimeType;
560
583
  this.cdnUrl = cdnUrl;
561
584
  this.cdnUrlModifiers = cdnUrlModifiers;
562
585
  this.originalUrl = originalUrl;
@@ -576,7 +599,7 @@ const poll = ({ check, interval = DEFAULT_INTERVAL, signal }) => new Promise((re
576
599
  const tick = () => {
577
600
  try {
578
601
  Promise.resolve(check(signal))
579
- .then(result => {
602
+ .then((result) => {
580
603
  if (result) {
581
604
  resolve(result);
582
605
  }
@@ -584,7 +607,7 @@ const poll = ({ check, interval = DEFAULT_INTERVAL, signal }) => new Promise((re
584
607
  timeoutId = setTimeout(tick, interval);
585
608
  }
586
609
  })
587
- .catch(error => reject(error));
610
+ .catch((error) => reject(error));
588
611
  }
589
612
  catch (error) {
590
613
  reject(error);
@@ -593,16 +616,17 @@ const poll = ({ check, interval = DEFAULT_INTERVAL, signal }) => new Promise((re
593
616
  timeoutId = setTimeout(tick, 0);
594
617
  });
595
618
 
596
- function isReadyPoll({ file, publicKey, baseURL, source, integration, retryThrottledRequestMaxTimes, signal, onProgress }) {
619
+ function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent, retryThrottledRequestMaxTimes, signal, onProgress }) {
597
620
  return poll({
598
- check: signal => info(file, {
621
+ check: (signal) => info(file, {
599
622
  publicKey,
600
623
  baseURL,
601
624
  signal,
602
625
  source,
603
626
  integration,
627
+ userAgent,
604
628
  retryThrottledRequestMaxTimes
605
- }).then(response => {
629
+ }).then((response) => {
606
630
  if (response.isReady) {
607
631
  return response;
608
632
  }
@@ -613,7 +637,7 @@ function isReadyPoll({ file, publicKey, baseURL, source, integration, retryThrot
613
637
  });
614
638
  }
615
639
 
616
- 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 }) => {
617
641
  return base(file, {
618
642
  publicKey,
619
643
  fileName,
@@ -625,6 +649,7 @@ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature,
625
649
  onProgress,
626
650
  source,
627
651
  integration,
652
+ userAgent,
628
653
  retryThrottledRequestMaxTimes
629
654
  })
630
655
  .then(({ file }) => {
@@ -634,12 +659,13 @@ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature,
634
659
  baseURL,
635
660
  source,
636
661
  integration,
662
+ userAgent,
637
663
  retryThrottledRequestMaxTimes,
638
664
  onProgress,
639
665
  signal
640
666
  });
641
667
  })
642
- .then(fileInfo => new UploadcareFile(fileInfo, { baseCDN }));
668
+ .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
643
669
  };
644
670
 
645
671
  /*globals self, window */
@@ -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, UploadcareFile, UploadcareGroup, base, fromUrl, fromUrlStatus, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadFromObject as uploadBase, uploadFile, uploadFileGroup, uploadFromUploaded, uploadFromUrl, uploadMultipart };