@uploadcare/upload-client 6.5.1 → 6.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,6 +23,7 @@ Node.js and browser.
23
23
  - [High-Level API](#high-level-api)
24
24
  - [Low-Level API](#low-level-api)
25
25
  - [Settings](#settings)
26
+ - [Uploading queue](#uploading-queue)
26
27
  - [React Native](#react-native)
27
28
  - [Testing](#testing)
28
29
  - [Security issues](#security-issues)
@@ -54,9 +55,7 @@ Once the UploadClient instance is created, you can start using the wrapper to
54
55
  upload files from binary data:
55
56
 
56
57
  ```javascript
57
- client
58
- .uploadFile(fileData)
59
- .then(file => console.log(file.uuid))
58
+ client.uploadFile(fileData).then((file) => console.log(file.uuid))
60
59
  ```
61
60
 
62
61
  Another option is uploading files from URL, via the `uploadFile` method:
@@ -64,9 +63,7 @@ Another option is uploading files from URL, via the `uploadFile` method:
64
63
  ```javascript
65
64
  const fileURL = 'https://example.com/file.jpg'
66
65
 
67
- client
68
- .uploadFile(fileURL)
69
- .then(file => console.log(file.uuid))
66
+ client.uploadFile(fileURL).then((file) => console.log(file.uuid))
70
67
  ```
71
68
 
72
69
  You can also use the `uploadFile` method to get previously uploaded files via
@@ -75,9 +72,7 @@ their UUIDs:
75
72
  ```javascript
76
73
  const fileUUID = 'edfdf045-34c0-4087-bbdd-e3834921f890'
77
74
 
78
- client
79
- .uploadFile(fileUUID)
80
- .then(file => console.log(file.uuid))
75
+ client.uploadFile(fileUUID).then((file) => console.log(file.uuid))
81
76
  ```
82
77
 
83
78
  You can track uploading progress:
@@ -90,7 +85,7 @@ const onProgress = ({ isComputable, value }) => {
90
85
 
91
86
  client
92
87
  .uploadFile(fileUUID, { onProgress })
93
- .then(file => console.log(file.uuid))
88
+ .then((file) => console.log(file.uuid))
94
89
  ```
95
90
 
96
91
  Note that `isComputable` flag can be `false` is some cases of uploading from the URL.
@@ -105,8 +100,8 @@ const abortController = new AbortController()
105
100
 
106
101
  client
107
102
  .uploadFile(fileUUID, { signal: abortController.signal })
108
- .then(file => console.log(file.uuid))
109
- .catch(error => {
103
+ .then((file) => console.log(file.uuid))
104
+ .catch((error) => {
110
105
  if (error.isCancel) {
111
106
  console.log(`File uploading was canceled.`)
112
107
  }
@@ -194,8 +189,8 @@ const onProgress = ({ isComputable, value }) => console.log(isComputable, value)
194
189
  const abortController = new AbortController()
195
190
 
196
191
  base(fileData, { onProgress, signal: abortController.signal }) // fileData must be `Blob` or `File` or `Buffer`
197
- .then(data => console.log(data.file))
198
- .catch(error => {
192
+ .then((data) => console.log(data.file))
193
+ .catch((error) => {
199
194
  if (error.isCancel) {
200
195
  console.log(`File uploading was canceled.`)
201
196
  }
@@ -421,6 +416,55 @@ Non-string values will be converted to `string`. `undefined` values will be igno
421
416
 
422
417
  See [docs][uc-file-metadata] and [REST API][uc-docs-metadata] for details.
423
418
 
419
+ ### Uploading queue
420
+
421
+ If you're going to upload a lot of files at once, it's useful to do it in a queue. Otherwise, a large number of simultaneous requests can clog the internet channel and slow down the process.
422
+
423
+ To solve this problem, we provide a simple helper called `Queue`.
424
+
425
+ Here is an example of how to use it:
426
+
427
+ ```typescript
428
+ import { Queue, uploadFile } from '@uploadcare/upload-client'
429
+
430
+ // Create a queue with a limit of 10 concurrent requests.
431
+ const queue = new Queue(10)
432
+
433
+ // Create an array containing 50 files.
434
+ const files = [
435
+ ...Array(50)
436
+ .fill(0)
437
+ .map((_, idx) => Buffer.from(`content-${idx}`))
438
+ ]
439
+ const promises = files.map((file, idx) => {
440
+ const fileName = `file-${idx}.txt`
441
+ return queue
442
+ .add(() =>
443
+ uploadFile(file, {
444
+ publicKey: 'YOUR_PUBLIC_KEY',
445
+ contentType: 'plain/text',
446
+ fileName
447
+ })
448
+ )
449
+ .then((fileInfo) =>
450
+ console.log(
451
+ `"File "${fileName}" has been successfully uploaded! You can access it at the following URL: "${fileInfo.cdnUrl}"`
452
+ )
453
+ )
454
+ })
455
+
456
+ await Promise.all(promises)
457
+
458
+ console.log('Files have been successfully uploaded')
459
+ ```
460
+
461
+ You can pass any function that returns a promise to `queue.add`, and it will be executed concurrently.
462
+
463
+ `queue.add` returns a promise that mimics the one passed in, meaning it will resolve or reject with the corresponding values.
464
+
465
+ If the functionality of the built-in `Queue` is not sufficient for you, you can use any other third-party, more functional solution.
466
+
467
+
424
468
  ## React Native
425
469
 
426
470
  ### Prepare
@@ -281,15 +281,14 @@ const getFileOptions = ({ name }) => name ? [name] : [];
281
281
  const transformFile = identity;
282
282
  var getFormData = () => new FormData();
283
283
 
284
+ const isBuffer = (data) => false;
285
+
284
286
  const isBlob = (data) => {
285
287
  return typeof Blob !== 'undefined' && data instanceof Blob;
286
288
  };
287
289
  const isFile = (data) => {
288
290
  return typeof File !== 'undefined' && data instanceof File;
289
291
  };
290
- const isBuffer = (data) => {
291
- return typeof Buffer !== 'undefined' && data instanceof Buffer;
292
- };
293
292
  const isReactNativeAsset = (data) => {
294
293
  return (!!data &&
295
294
  typeof data === 'object' &&
@@ -298,7 +297,7 @@ const isReactNativeAsset = (data) => {
298
297
  typeof data.uri === 'string');
299
298
  };
300
299
  const isFileData = (data) => {
301
- return (isBlob(data) || isFile(data) || isBuffer(data) || isReactNativeAsset(data));
300
+ return (isBlob(data) || isFile(data) || isBuffer() || isReactNativeAsset(data));
302
301
  };
303
302
 
304
303
  const isSimpleValue = (value) => {
@@ -398,7 +397,7 @@ const getUrl = (base, path, query) => {
398
397
  return url.toString();
399
398
  };
400
399
 
401
- var version = '6.5.1';
400
+ var version = '6.6.1';
402
401
 
403
402
  const LIBRARY_NAME = 'UploadcareUploadClient';
404
403
  const LIBRARY_VERSION = version;
@@ -453,7 +452,7 @@ const getFileName = (file) => {
453
452
  if (isFile(file) && file.name) {
454
453
  filename = file.name;
455
454
  }
456
- else if (isBlob(file) || isBuffer(file)) {
455
+ else if (isBlob(file) || isBuffer()) {
457
456
  filename = '';
458
457
  }
459
458
  else if (isReactNativeAsset(file) && file.name) {
@@ -1218,9 +1217,6 @@ const getBlobFromReactNativeAsset = async (asset) => {
1218
1217
  };
1219
1218
 
1220
1219
  const getFileSize = async (file) => {
1221
- if (isBuffer(file)) {
1222
- return file.length;
1223
- }
1224
1220
  if (isFile(file) || isBlob(file)) {
1225
1221
  return file.size;
1226
1222
  }
@@ -1656,6 +1652,62 @@ class UploadClient {
1656
1652
  }
1657
1653
  }
1658
1654
 
1655
+ class Queue {
1656
+ #concurrency = 1;
1657
+ #pending = [];
1658
+ #running = 0;
1659
+ #resolvers = new WeakMap();
1660
+ #rejectors = new WeakMap();
1661
+ constructor(concurrency) {
1662
+ this.#concurrency = concurrency;
1663
+ }
1664
+ #run() {
1665
+ const tasksLeft = this.#concurrency - this.#running;
1666
+ for (let i = 0; i < tasksLeft; i++) {
1667
+ const task = this.#pending.shift();
1668
+ if (!task) {
1669
+ return;
1670
+ }
1671
+ const resolver = this.#resolvers.get(task);
1672
+ const rejector = this.#rejectors.get(task);
1673
+ if (!resolver || !rejector)
1674
+ throw new Error('Unexpected behavior: resolver or rejector is undefined');
1675
+ this.#running += 1;
1676
+ task()
1677
+ .finally(() => {
1678
+ this.#resolvers.delete(task);
1679
+ this.#rejectors.delete(task);
1680
+ this.#running -= 1;
1681
+ this.#run();
1682
+ })
1683
+ .then((value) => resolver(value))
1684
+ .catch((error) => rejector(error));
1685
+ }
1686
+ }
1687
+ add(task) {
1688
+ return new Promise((resolve, reject) => {
1689
+ this.#resolvers.set(task, resolve);
1690
+ this.#rejectors.set(task, reject);
1691
+ this.#pending.push(task);
1692
+ this.#run();
1693
+ });
1694
+ }
1695
+ get pending() {
1696
+ return this.#pending.length;
1697
+ }
1698
+ get running() {
1699
+ return this.#running;
1700
+ }
1701
+ set concurrency(value) {
1702
+ this.#concurrency = value;
1703
+ this.#run();
1704
+ }
1705
+ get concurrency() {
1706
+ return this.#concurrency;
1707
+ }
1708
+ }
1709
+
1710
+ exports.Queue = Queue;
1659
1711
  exports.UploadClient = UploadClient;
1660
1712
  exports.UploadClientError = UploadClientError;
1661
1713
  exports.UploadcareFile = UploadcareFile;
@@ -472,6 +472,16 @@ export declare class UploadClient {
472
472
  uploadFile(data: SupportedFileInput | Url | Uuid, options?: Partial<FileFromOptions>): Promise<UploadcareFile>;
473
473
  uploadFileGroup(data: SupportedFileInput[] | Url[] | Uuid[], options?: Partial<FileFromOptions & GroupFromOptions>): Promise<UploadcareGroup>;
474
474
  }
475
+ export type Task<T = unknown> = () => Promise<T>;
476
+ export declare class Queue {
477
+ #private;
478
+ constructor(concurrency: number);
479
+ add<T>(task: Task<T>): Promise<T>;
480
+ get pending(): number;
481
+ get running(): number;
482
+ set concurrency(value: number);
483
+ get concurrency(): number;
484
+ }
475
485
  export type Headers = {
476
486
  [key: string]: string | string[] | undefined;
477
487
  };
@@ -312,15 +312,14 @@ const getFileOptions = ({ name, contentType }) => [
312
312
  const transformFile = identity;
313
313
  var getFormData = () => new NodeFormData();
314
314
 
315
+ const isBuffer = (data) => data instanceof Buffer;
316
+
315
317
  const isBlob = (data) => {
316
318
  return typeof Blob !== 'undefined' && data instanceof Blob;
317
319
  };
318
320
  const isFile = (data) => {
319
321
  return typeof File !== 'undefined' && data instanceof File;
320
322
  };
321
- const isBuffer = (data) => {
322
- return typeof Buffer !== 'undefined' && data instanceof Buffer;
323
- };
324
323
  const isReactNativeAsset = (data) => {
325
324
  return (!!data &&
326
325
  typeof data === 'object' &&
@@ -429,7 +428,7 @@ const getUrl = (base, path, query) => {
429
428
  return url.toString();
430
429
  };
431
430
 
432
- var version = '6.5.1';
431
+ var version = '6.6.1';
433
432
 
434
433
  const LIBRARY_NAME = 'UploadcareUploadClient';
435
434
  const LIBRARY_VERSION = version;
@@ -1685,6 +1684,62 @@ class UploadClient {
1685
1684
  }
1686
1685
  }
1687
1686
 
1687
+ class Queue {
1688
+ #concurrency = 1;
1689
+ #pending = [];
1690
+ #running = 0;
1691
+ #resolvers = new WeakMap();
1692
+ #rejectors = new WeakMap();
1693
+ constructor(concurrency) {
1694
+ this.#concurrency = concurrency;
1695
+ }
1696
+ #run() {
1697
+ const tasksLeft = this.#concurrency - this.#running;
1698
+ for (let i = 0; i < tasksLeft; i++) {
1699
+ const task = this.#pending.shift();
1700
+ if (!task) {
1701
+ return;
1702
+ }
1703
+ const resolver = this.#resolvers.get(task);
1704
+ const rejector = this.#rejectors.get(task);
1705
+ if (!resolver || !rejector)
1706
+ throw new Error('Unexpected behavior: resolver or rejector is undefined');
1707
+ this.#running += 1;
1708
+ task()
1709
+ .finally(() => {
1710
+ this.#resolvers.delete(task);
1711
+ this.#rejectors.delete(task);
1712
+ this.#running -= 1;
1713
+ this.#run();
1714
+ })
1715
+ .then((value) => resolver(value))
1716
+ .catch((error) => rejector(error));
1717
+ }
1718
+ }
1719
+ add(task) {
1720
+ return new Promise((resolve, reject) => {
1721
+ this.#resolvers.set(task, resolve);
1722
+ this.#rejectors.set(task, reject);
1723
+ this.#pending.push(task);
1724
+ this.#run();
1725
+ });
1726
+ }
1727
+ get pending() {
1728
+ return this.#pending.length;
1729
+ }
1730
+ get running() {
1731
+ return this.#running;
1732
+ }
1733
+ set concurrency(value) {
1734
+ this.#concurrency = value;
1735
+ this.#run();
1736
+ }
1737
+ get concurrency() {
1738
+ return this.#concurrency;
1739
+ }
1740
+ }
1741
+
1742
+ exports.Queue = Queue;
1688
1743
  exports.UploadClient = UploadClient;
1689
1744
  exports.UploadClientError = UploadClientError;
1690
1745
  exports.UploadcareFile = UploadcareFile;
@@ -472,6 +472,16 @@ export declare class UploadClient {
472
472
  uploadFile(data: SupportedFileInput | Url | Uuid, options?: Partial<FileFromOptions>): Promise<UploadcareFile>;
473
473
  uploadFileGroup(data: SupportedFileInput[] | Url[] | Uuid[], options?: Partial<FileFromOptions & GroupFromOptions>): Promise<UploadcareGroup>;
474
474
  }
475
+ export type Task<T = unknown> = () => Promise<T>;
476
+ export declare class Queue {
477
+ #private;
478
+ constructor(concurrency: number);
479
+ add<T>(task: Task<T>): Promise<T>;
480
+ get pending(): number;
481
+ get running(): number;
482
+ set concurrency(value: number);
483
+ get concurrency(): number;
484
+ }
475
485
  export type Headers = {
476
486
  [key: string]: string | string[] | undefined;
477
487
  };
@@ -272,15 +272,14 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
272
272
  }
273
273
  });
274
274
 
275
+ const isBuffer = (data) => false;
276
+
275
277
  const isBlob = (data) => {
276
278
  return typeof Blob !== 'undefined' && data instanceof Blob;
277
279
  };
278
280
  const isFile = (data) => {
279
281
  return typeof File !== 'undefined' && data instanceof File;
280
282
  };
281
- const isBuffer = (data) => {
282
- return typeof Buffer !== 'undefined' && data instanceof Buffer;
283
- };
284
283
  const isReactNativeAsset = (data) => {
285
284
  return (!!data &&
286
285
  typeof data === 'object' &&
@@ -289,7 +288,7 @@ const isReactNativeAsset = (data) => {
289
288
  typeof data.uri === 'string');
290
289
  };
291
290
  const isFileData = (data) => {
292
- return (isBlob(data) || isFile(data) || isBuffer(data) || isReactNativeAsset(data));
291
+ return (isBlob(data) || isFile(data) || isBuffer() || isReactNativeAsset(data));
293
292
  };
294
293
 
295
294
  const getFileOptions = () => [];
@@ -406,7 +405,7 @@ const getUrl = (base, path, query) => {
406
405
  return url.toString();
407
406
  };
408
407
 
409
- var version = '6.5.1';
408
+ var version = '6.6.1';
410
409
 
411
410
  const LIBRARY_NAME = 'UploadcareUploadClient';
412
411
  const LIBRARY_VERSION = version;
@@ -461,7 +460,7 @@ const getFileName = (file) => {
461
460
  if (isFile(file) && file.name) {
462
461
  filename = file.name;
463
462
  }
464
- else if (isBlob(file) || isBuffer(file)) {
463
+ else if (isBlob(file) || isBuffer()) {
465
464
  filename = '';
466
465
  }
467
466
  else if (isReactNativeAsset(file) && file.name) {
@@ -1226,9 +1225,6 @@ const getBlobFromReactNativeAsset = async (asset) => {
1226
1225
  };
1227
1226
 
1228
1227
  const getFileSize = async (file) => {
1229
- if (isBuffer(file)) {
1230
- return file.length;
1231
- }
1232
1228
  if (isFile(file) || isBlob(file)) {
1233
1229
  return file.size;
1234
1230
  }
@@ -1684,6 +1680,62 @@ class UploadClient {
1684
1680
  }
1685
1681
  }
1686
1682
 
1683
+ class Queue {
1684
+ #concurrency = 1;
1685
+ #pending = [];
1686
+ #running = 0;
1687
+ #resolvers = new WeakMap();
1688
+ #rejectors = new WeakMap();
1689
+ constructor(concurrency) {
1690
+ this.#concurrency = concurrency;
1691
+ }
1692
+ #run() {
1693
+ const tasksLeft = this.#concurrency - this.#running;
1694
+ for (let i = 0; i < tasksLeft; i++) {
1695
+ const task = this.#pending.shift();
1696
+ if (!task) {
1697
+ return;
1698
+ }
1699
+ const resolver = this.#resolvers.get(task);
1700
+ const rejector = this.#rejectors.get(task);
1701
+ if (!resolver || !rejector)
1702
+ throw new Error('Unexpected behavior: resolver or rejector is undefined');
1703
+ this.#running += 1;
1704
+ task()
1705
+ .finally(() => {
1706
+ this.#resolvers.delete(task);
1707
+ this.#rejectors.delete(task);
1708
+ this.#running -= 1;
1709
+ this.#run();
1710
+ })
1711
+ .then((value) => resolver(value))
1712
+ .catch((error) => rejector(error));
1713
+ }
1714
+ }
1715
+ add(task) {
1716
+ return new Promise((resolve, reject) => {
1717
+ this.#resolvers.set(task, resolve);
1718
+ this.#rejectors.set(task, reject);
1719
+ this.#pending.push(task);
1720
+ this.#run();
1721
+ });
1722
+ }
1723
+ get pending() {
1724
+ return this.#pending.length;
1725
+ }
1726
+ get running() {
1727
+ return this.#running;
1728
+ }
1729
+ set concurrency(value) {
1730
+ this.#concurrency = value;
1731
+ this.#run();
1732
+ }
1733
+ get concurrency() {
1734
+ return this.#concurrency;
1735
+ }
1736
+ }
1737
+
1738
+ exports.Queue = Queue;
1687
1739
  exports.UploadClient = UploadClient;
1688
1740
  exports.UploadClientError = UploadClientError;
1689
1741
  exports.UploadcareFile = UploadcareFile;
@@ -472,6 +472,16 @@ export declare class UploadClient {
472
472
  uploadFile(data: SupportedFileInput | Url | Uuid, options?: Partial<FileFromOptions>): Promise<UploadcareFile>;
473
473
  uploadFileGroup(data: SupportedFileInput[] | Url[] | Uuid[], options?: Partial<FileFromOptions & GroupFromOptions>): Promise<UploadcareGroup>;
474
474
  }
475
+ export type Task<T = unknown> = () => Promise<T>;
476
+ export declare class Queue {
477
+ #private;
478
+ constructor(concurrency: number);
479
+ add<T>(task: Task<T>): Promise<T>;
480
+ get pending(): number;
481
+ get running(): number;
482
+ set concurrency(value: number);
483
+ get concurrency(): number;
484
+ }
475
485
  export type Headers = {
476
486
  [key: string]: string | string[] | undefined;
477
487
  };
@@ -472,6 +472,16 @@ export declare class UploadClient {
472
472
  uploadFile(data: SupportedFileInput | Url | Uuid, options?: Partial<FileFromOptions>): Promise<UploadcareFile>;
473
473
  uploadFileGroup(data: SupportedFileInput[] | Url[] | Uuid[], options?: Partial<FileFromOptions & GroupFromOptions>): Promise<UploadcareGroup>;
474
474
  }
475
+ export type Task<T = unknown> = () => Promise<T>;
476
+ export declare class Queue {
477
+ #private;
478
+ constructor(concurrency: number);
479
+ add<T>(task: Task<T>): Promise<T>;
480
+ get pending(): number;
481
+ get running(): number;
482
+ set concurrency(value: number);
483
+ get concurrency(): number;
484
+ }
475
485
  export type Headers = {
476
486
  [key: string]: string | string[] | undefined;
477
487
  };
@@ -279,15 +279,14 @@ const getFileOptions = ({ name }) => name ? [name] : [];
279
279
  const transformFile = identity;
280
280
  var getFormData = () => new FormData();
281
281
 
282
+ const isBuffer = (data) => false;
283
+
282
284
  const isBlob = (data) => {
283
285
  return typeof Blob !== 'undefined' && data instanceof Blob;
284
286
  };
285
287
  const isFile = (data) => {
286
288
  return typeof File !== 'undefined' && data instanceof File;
287
289
  };
288
- const isBuffer = (data) => {
289
- return typeof Buffer !== 'undefined' && data instanceof Buffer;
290
- };
291
290
  const isReactNativeAsset = (data) => {
292
291
  return (!!data &&
293
292
  typeof data === 'object' &&
@@ -296,7 +295,7 @@ const isReactNativeAsset = (data) => {
296
295
  typeof data.uri === 'string');
297
296
  };
298
297
  const isFileData = (data) => {
299
- return (isBlob(data) || isFile(data) || isBuffer(data) || isReactNativeAsset(data));
298
+ return (isBlob(data) || isFile(data) || isBuffer() || isReactNativeAsset(data));
300
299
  };
301
300
 
302
301
  const isSimpleValue = (value) => {
@@ -396,7 +395,7 @@ const getUrl = (base, path, query) => {
396
395
  return url.toString();
397
396
  };
398
397
 
399
- var version = '6.5.1';
398
+ var version = '6.6.1';
400
399
 
401
400
  const LIBRARY_NAME = 'UploadcareUploadClient';
402
401
  const LIBRARY_VERSION = version;
@@ -451,7 +450,7 @@ const getFileName = (file) => {
451
450
  if (isFile(file) && file.name) {
452
451
  filename = file.name;
453
452
  }
454
- else if (isBlob(file) || isBuffer(file)) {
453
+ else if (isBlob(file) || isBuffer()) {
455
454
  filename = '';
456
455
  }
457
456
  else if (isReactNativeAsset(file) && file.name) {
@@ -1216,9 +1215,6 @@ const getBlobFromReactNativeAsset = async (asset) => {
1216
1215
  };
1217
1216
 
1218
1217
  const getFileSize = async (file) => {
1219
- if (isBuffer(file)) {
1220
- return file.length;
1221
- }
1222
1218
  if (isFile(file) || isBlob(file)) {
1223
1219
  return file.size;
1224
1220
  }
@@ -1654,4 +1650,59 @@ class UploadClient {
1654
1650
  }
1655
1651
  }
1656
1652
 
1657
- export { UploadClient, UploadClientError, UploadcareFile, UploadcareGroup, UploadcareNetworkError, base, fromUrl, fromUrlStatus, getUserAgent$1 as getUserAgent, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadDirect, uploadFile, uploadFileGroup, uploadFromUploaded, uploadFromUrl, uploadMultipart };
1653
+ class Queue {
1654
+ #concurrency = 1;
1655
+ #pending = [];
1656
+ #running = 0;
1657
+ #resolvers = new WeakMap();
1658
+ #rejectors = new WeakMap();
1659
+ constructor(concurrency) {
1660
+ this.#concurrency = concurrency;
1661
+ }
1662
+ #run() {
1663
+ const tasksLeft = this.#concurrency - this.#running;
1664
+ for (let i = 0; i < tasksLeft; i++) {
1665
+ const task = this.#pending.shift();
1666
+ if (!task) {
1667
+ return;
1668
+ }
1669
+ const resolver = this.#resolvers.get(task);
1670
+ const rejector = this.#rejectors.get(task);
1671
+ if (!resolver || !rejector)
1672
+ throw new Error('Unexpected behavior: resolver or rejector is undefined');
1673
+ this.#running += 1;
1674
+ task()
1675
+ .finally(() => {
1676
+ this.#resolvers.delete(task);
1677
+ this.#rejectors.delete(task);
1678
+ this.#running -= 1;
1679
+ this.#run();
1680
+ })
1681
+ .then((value) => resolver(value))
1682
+ .catch((error) => rejector(error));
1683
+ }
1684
+ }
1685
+ add(task) {
1686
+ return new Promise((resolve, reject) => {
1687
+ this.#resolvers.set(task, resolve);
1688
+ this.#rejectors.set(task, reject);
1689
+ this.#pending.push(task);
1690
+ this.#run();
1691
+ });
1692
+ }
1693
+ get pending() {
1694
+ return this.#pending.length;
1695
+ }
1696
+ get running() {
1697
+ return this.#running;
1698
+ }
1699
+ set concurrency(value) {
1700
+ this.#concurrency = value;
1701
+ this.#run();
1702
+ }
1703
+ get concurrency() {
1704
+ return this.#concurrency;
1705
+ }
1706
+ }
1707
+
1708
+ export { Queue, UploadClient, UploadClientError, UploadcareFile, UploadcareGroup, UploadcareNetworkError, base, fromUrl, fromUrlStatus, getUserAgent$1 as getUserAgent, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadDirect, uploadFile, uploadFileGroup, uploadFromUploaded, uploadFromUrl, uploadMultipart };
@@ -472,6 +472,16 @@ export declare class UploadClient {
472
472
  uploadFile(data: SupportedFileInput | Url | Uuid, options?: Partial<FileFromOptions>): Promise<UploadcareFile>;
473
473
  uploadFileGroup(data: SupportedFileInput[] | Url[] | Uuid[], options?: Partial<FileFromOptions & GroupFromOptions>): Promise<UploadcareGroup>;
474
474
  }
475
+ export type Task<T = unknown> = () => Promise<T>;
476
+ export declare class Queue {
477
+ #private;
478
+ constructor(concurrency: number);
479
+ add<T>(task: Task<T>): Promise<T>;
480
+ get pending(): number;
481
+ get running(): number;
482
+ set concurrency(value: number);
483
+ get concurrency(): number;
484
+ }
475
485
  export type Headers = {
476
486
  [key: string]: string | string[] | undefined;
477
487
  };
@@ -310,15 +310,14 @@ const getFileOptions = ({ name, contentType }) => [
310
310
  const transformFile = identity;
311
311
  var getFormData = () => new NodeFormData();
312
312
 
313
+ const isBuffer = (data) => data instanceof Buffer;
314
+
313
315
  const isBlob = (data) => {
314
316
  return typeof Blob !== 'undefined' && data instanceof Blob;
315
317
  };
316
318
  const isFile = (data) => {
317
319
  return typeof File !== 'undefined' && data instanceof File;
318
320
  };
319
- const isBuffer = (data) => {
320
- return typeof Buffer !== 'undefined' && data instanceof Buffer;
321
- };
322
321
  const isReactNativeAsset = (data) => {
323
322
  return (!!data &&
324
323
  typeof data === 'object' &&
@@ -427,7 +426,7 @@ const getUrl = (base, path, query) => {
427
426
  return url.toString();
428
427
  };
429
428
 
430
- var version = '6.5.1';
429
+ var version = '6.6.1';
431
430
 
432
431
  const LIBRARY_NAME = 'UploadcareUploadClient';
433
432
  const LIBRARY_VERSION = version;
@@ -1683,4 +1682,59 @@ class UploadClient {
1683
1682
  }
1684
1683
  }
1685
1684
 
1686
- export { UploadClient, UploadClientError, UploadcareFile, UploadcareGroup, UploadcareNetworkError, base, fromUrl, fromUrlStatus, getUserAgent$1 as getUserAgent, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadDirect, uploadFile, uploadFileGroup, uploadFromUploaded, uploadFromUrl, uploadMultipart };
1685
+ class Queue {
1686
+ #concurrency = 1;
1687
+ #pending = [];
1688
+ #running = 0;
1689
+ #resolvers = new WeakMap();
1690
+ #rejectors = new WeakMap();
1691
+ constructor(concurrency) {
1692
+ this.#concurrency = concurrency;
1693
+ }
1694
+ #run() {
1695
+ const tasksLeft = this.#concurrency - this.#running;
1696
+ for (let i = 0; i < tasksLeft; i++) {
1697
+ const task = this.#pending.shift();
1698
+ if (!task) {
1699
+ return;
1700
+ }
1701
+ const resolver = this.#resolvers.get(task);
1702
+ const rejector = this.#rejectors.get(task);
1703
+ if (!resolver || !rejector)
1704
+ throw new Error('Unexpected behavior: resolver or rejector is undefined');
1705
+ this.#running += 1;
1706
+ task()
1707
+ .finally(() => {
1708
+ this.#resolvers.delete(task);
1709
+ this.#rejectors.delete(task);
1710
+ this.#running -= 1;
1711
+ this.#run();
1712
+ })
1713
+ .then((value) => resolver(value))
1714
+ .catch((error) => rejector(error));
1715
+ }
1716
+ }
1717
+ add(task) {
1718
+ return new Promise((resolve, reject) => {
1719
+ this.#resolvers.set(task, resolve);
1720
+ this.#rejectors.set(task, reject);
1721
+ this.#pending.push(task);
1722
+ this.#run();
1723
+ });
1724
+ }
1725
+ get pending() {
1726
+ return this.#pending.length;
1727
+ }
1728
+ get running() {
1729
+ return this.#running;
1730
+ }
1731
+ set concurrency(value) {
1732
+ this.#concurrency = value;
1733
+ this.#run();
1734
+ }
1735
+ get concurrency() {
1736
+ return this.#concurrency;
1737
+ }
1738
+ }
1739
+
1740
+ export { Queue, UploadClient, UploadClientError, UploadcareFile, UploadcareGroup, UploadcareNetworkError, base, fromUrl, fromUrlStatus, getUserAgent$1 as getUserAgent, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadDirect, uploadFile, uploadFileGroup, uploadFromUploaded, uploadFromUrl, uploadMultipart };
@@ -472,6 +472,16 @@ export declare class UploadClient {
472
472
  uploadFile(data: SupportedFileInput | Url | Uuid, options?: Partial<FileFromOptions>): Promise<UploadcareFile>;
473
473
  uploadFileGroup(data: SupportedFileInput[] | Url[] | Uuid[], options?: Partial<FileFromOptions & GroupFromOptions>): Promise<UploadcareGroup>;
474
474
  }
475
+ export type Task<T = unknown> = () => Promise<T>;
476
+ export declare class Queue {
477
+ #private;
478
+ constructor(concurrency: number);
479
+ add<T>(task: Task<T>): Promise<T>;
480
+ get pending(): number;
481
+ get running(): number;
482
+ set concurrency(value: number);
483
+ get concurrency(): number;
484
+ }
475
485
  export type Headers = {
476
486
  [key: string]: string | string[] | undefined;
477
487
  };
@@ -270,15 +270,14 @@ const request = ({ method, url, data, headers = {}, signal, onProgress }) => new
270
270
  }
271
271
  });
272
272
 
273
+ const isBuffer = (data) => false;
274
+
273
275
  const isBlob = (data) => {
274
276
  return typeof Blob !== 'undefined' && data instanceof Blob;
275
277
  };
276
278
  const isFile = (data) => {
277
279
  return typeof File !== 'undefined' && data instanceof File;
278
280
  };
279
- const isBuffer = (data) => {
280
- return typeof Buffer !== 'undefined' && data instanceof Buffer;
281
- };
282
281
  const isReactNativeAsset = (data) => {
283
282
  return (!!data &&
284
283
  typeof data === 'object' &&
@@ -287,7 +286,7 @@ const isReactNativeAsset = (data) => {
287
286
  typeof data.uri === 'string');
288
287
  };
289
288
  const isFileData = (data) => {
290
- return (isBlob(data) || isFile(data) || isBuffer(data) || isReactNativeAsset(data));
289
+ return (isBlob(data) || isFile(data) || isBuffer() || isReactNativeAsset(data));
291
290
  };
292
291
 
293
292
  const getFileOptions = () => [];
@@ -404,7 +403,7 @@ const getUrl = (base, path, query) => {
404
403
  return url.toString();
405
404
  };
406
405
 
407
- var version = '6.5.1';
406
+ var version = '6.6.1';
408
407
 
409
408
  const LIBRARY_NAME = 'UploadcareUploadClient';
410
409
  const LIBRARY_VERSION = version;
@@ -459,7 +458,7 @@ const getFileName = (file) => {
459
458
  if (isFile(file) && file.name) {
460
459
  filename = file.name;
461
460
  }
462
- else if (isBlob(file) || isBuffer(file)) {
461
+ else if (isBlob(file) || isBuffer()) {
463
462
  filename = '';
464
463
  }
465
464
  else if (isReactNativeAsset(file) && file.name) {
@@ -1224,9 +1223,6 @@ const getBlobFromReactNativeAsset = async (asset) => {
1224
1223
  };
1225
1224
 
1226
1225
  const getFileSize = async (file) => {
1227
- if (isBuffer(file)) {
1228
- return file.length;
1229
- }
1230
1226
  if (isFile(file) || isBlob(file)) {
1231
1227
  return file.size;
1232
1228
  }
@@ -1682,4 +1678,59 @@ class UploadClient {
1682
1678
  }
1683
1679
  }
1684
1680
 
1685
- export { UploadClient, UploadClientError, UploadcareFile, UploadcareGroup, UploadcareNetworkError, base, fromUrl, fromUrlStatus, getUserAgent$1 as getUserAgent, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadDirect, uploadFile, uploadFileGroup, uploadFromUploaded, uploadFromUrl, uploadMultipart };
1681
+ class Queue {
1682
+ #concurrency = 1;
1683
+ #pending = [];
1684
+ #running = 0;
1685
+ #resolvers = new WeakMap();
1686
+ #rejectors = new WeakMap();
1687
+ constructor(concurrency) {
1688
+ this.#concurrency = concurrency;
1689
+ }
1690
+ #run() {
1691
+ const tasksLeft = this.#concurrency - this.#running;
1692
+ for (let i = 0; i < tasksLeft; i++) {
1693
+ const task = this.#pending.shift();
1694
+ if (!task) {
1695
+ return;
1696
+ }
1697
+ const resolver = this.#resolvers.get(task);
1698
+ const rejector = this.#rejectors.get(task);
1699
+ if (!resolver || !rejector)
1700
+ throw new Error('Unexpected behavior: resolver or rejector is undefined');
1701
+ this.#running += 1;
1702
+ task()
1703
+ .finally(() => {
1704
+ this.#resolvers.delete(task);
1705
+ this.#rejectors.delete(task);
1706
+ this.#running -= 1;
1707
+ this.#run();
1708
+ })
1709
+ .then((value) => resolver(value))
1710
+ .catch((error) => rejector(error));
1711
+ }
1712
+ }
1713
+ add(task) {
1714
+ return new Promise((resolve, reject) => {
1715
+ this.#resolvers.set(task, resolve);
1716
+ this.#rejectors.set(task, reject);
1717
+ this.#pending.push(task);
1718
+ this.#run();
1719
+ });
1720
+ }
1721
+ get pending() {
1722
+ return this.#pending.length;
1723
+ }
1724
+ get running() {
1725
+ return this.#running;
1726
+ }
1727
+ set concurrency(value) {
1728
+ this.#concurrency = value;
1729
+ this.#run();
1730
+ }
1731
+ get concurrency() {
1732
+ return this.#concurrency;
1733
+ }
1734
+ }
1735
+
1736
+ export { Queue, UploadClient, UploadClientError, UploadcareFile, UploadcareGroup, UploadcareNetworkError, base, fromUrl, fromUrlStatus, getUserAgent$1 as getUserAgent, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadDirect, uploadFile, uploadFileGroup, uploadFromUploaded, uploadFromUrl, uploadMultipart };
package/dist/index.d.ts CHANGED
@@ -472,6 +472,16 @@ export declare class UploadClient {
472
472
  uploadFile(data: SupportedFileInput | Url | Uuid, options?: Partial<FileFromOptions>): Promise<UploadcareFile>;
473
473
  uploadFileGroup(data: SupportedFileInput[] | Url[] | Uuid[], options?: Partial<FileFromOptions & GroupFromOptions>): Promise<UploadcareGroup>;
474
474
  }
475
+ export type Task<T = unknown> = () => Promise<T>;
476
+ export declare class Queue {
477
+ #private;
478
+ constructor(concurrency: number);
479
+ add<T>(task: Task<T>): Promise<T>;
480
+ get pending(): number;
481
+ get running(): number;
482
+ set concurrency(value: number);
483
+ get concurrency(): number;
484
+ }
475
485
  export type Headers = {
476
486
  [key: string]: string | string[] | undefined;
477
487
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uploadcare/upload-client",
3
- "version": "6.5.1",
3
+ "version": "6.6.1",
4
4
  "description": "Library for work with Uploadcare Upload API",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.node.mjs",
@@ -90,7 +90,7 @@
90
90
  "koa-body": "5.0.0",
91
91
  "mock-socket": "9.0.3",
92
92
  "start-server-and-test": "1.14.0",
93
- "@uploadcare/api-client-utils": "^6.5.1",
93
+ "@uploadcare/api-client-utils": "^6.6.1",
94
94
  "chalk": "^4.1.2"
95
95
  },
96
96
  "dependencies": {