@uploadcare/upload-client 4.0.1 → 4.2.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 +13 -11
- package/dist/index.browser.js +140 -119
- package/dist/index.node.js +140 -119
- package/dist/index.react-native.js +141 -120
- package/dist/types.d.ts +45 -46
- package/package.json +27 -45
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Uploadcare Upload Client
|
|
2
2
|
|
|
3
|
-
<a href="https://uploadcare.com/?utm_source=github&utm_campaign=uploadcare-
|
|
3
|
+
<a href="https://uploadcare.com/?utm_source=github&utm_campaign=uploadcare-js-api-clients">
|
|
4
4
|
<img align="right" width="64" height="64"
|
|
5
5
|
src="https://ucarecdn.com/edfdf045-34c0-4087-bbdd-e3834921f890/userpiccircletransparent.svg"
|
|
6
6
|
alt="">
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
This is an Uploadcare [Upload API][uc-docs-upload-api] wrapper to work with
|
|
10
10
|
Node.js and browser.
|
|
11
11
|
|
|
12
|
+
[API Reference](https://uploadcare.github.io/uploadcare-js-api-clients/upload-client/)
|
|
13
|
+
|
|
12
14
|
[![Build Status][badge-build]][build-url]
|
|
13
15
|
[![NPM version][npm-img]][npm-url]
|
|
14
16
|
[![GitHub release][badge-release-img]][badge-release-url]
|
|
@@ -227,11 +229,11 @@ fromUrlStatus(
|
|
|
227
229
|
```
|
|
228
230
|
|
|
229
231
|
```typescript
|
|
230
|
-
|
|
232
|
+
group(uuids: Uuid[], options: GroupOptions): Promise<GroupInfo>
|
|
231
233
|
```
|
|
232
234
|
|
|
233
235
|
```typescript
|
|
234
|
-
|
|
236
|
+
groupInfo(id: GroupId, options: GroupInfoOptions): Promise<GroupInfo>
|
|
235
237
|
```
|
|
236
238
|
|
|
237
239
|
```typescript
|
|
@@ -461,16 +463,16 @@ request at [hello@uploadcare.com][uc-email-hello].
|
|
|
461
463
|
|
|
462
464
|
[uc-email-bounty]: mailto:bugbounty@uploadcare.com
|
|
463
465
|
[uc-email-hello]: mailto:hello@uploadcare.com
|
|
464
|
-
[github-releases]: https://github.com/uploadcare/uploadcare-
|
|
465
|
-
[github-branch-release]: https://github.com/uploadcare/uploadcare-
|
|
466
|
-
[github-contributors]: https://github.com/uploadcare/uploadcare-
|
|
466
|
+
[github-releases]: https://github.com/uploadcare/uploadcare-js-api-clients/releases
|
|
467
|
+
[github-branch-release]: https://github.com/uploadcare/uploadcare-js-api-clients/tree/release
|
|
468
|
+
[github-contributors]: https://github.com/uploadcare/uploadcare-js-api-clients/graphs/contributors
|
|
467
469
|
[badge-stack-img]: https://img.shields.io/badge/tech-stack-0690fa.svg?style=flat
|
|
468
470
|
[badge-stack-url]: https://stackshare.io/uploadcare/stacks/
|
|
469
|
-
[badge-release-img]: https://img.shields.io/github/release/uploadcare/uploadcare-
|
|
470
|
-
[badge-release-url]: https://github.com/uploadcare/uploadcare-
|
|
471
|
+
[badge-release-img]: https://img.shields.io/github/release/uploadcare/uploadcare-js-api-clients.svg
|
|
472
|
+
[badge-release-url]: https://github.com/uploadcare/uploadcare-js-api-clients/releases
|
|
471
473
|
[npm-img]: http://img.shields.io/npm/v/@uploadcare/upload-client.svg
|
|
472
474
|
[npm-url]: https://www.npmjs.org/package/@uploadcare/upload-client
|
|
473
|
-
[badge-build]: https://github.com/uploadcare/uploadcare-
|
|
474
|
-
[build-url]: https://github.com/uploadcare/uploadcare-
|
|
475
|
-
[uc-docs-upload-api]: https://uploadcare.com/docs/api_reference/upload/?utm_source=github&utm_campaign=uploadcare-
|
|
475
|
+
[badge-build]: https://github.com/uploadcare/uploadcare-js-api-clients/actions/workflows/checks.yml/badge.svg
|
|
476
|
+
[build-url]: https://github.com/uploadcare/uploadcare-js-api-clients/actions/workflows/checks.yml
|
|
477
|
+
[uc-docs-upload-api]: https://uploadcare.com/docs/api_reference/upload/?utm_source=github&utm_campaign=uploadcare-js-api-clients
|
|
476
478
|
[uc-docs-metadata]: https://uploadcare.com/api-refs/rest-api/v0.7.0/#tag/File-Metadata
|
package/dist/index.browser.js
CHANGED
|
@@ -29,7 +29,7 @@ const onCancel = (signal, callback) => {
|
|
|
29
29
|
|
|
30
30
|
const request = ({ method, url, data, headers = {}, signal, onProgress }) => new Promise((resolve, reject) => {
|
|
31
31
|
const xhr = new XMLHttpRequest();
|
|
32
|
-
const requestMethod =
|
|
32
|
+
const requestMethod = method?.toUpperCase() || 'GET';
|
|
33
33
|
let aborted = false;
|
|
34
34
|
xhr.open(requestMethod, url);
|
|
35
35
|
if (headers) {
|
|
@@ -197,34 +197,36 @@ function buildFormData(options) {
|
|
|
197
197
|
return formData;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
200
|
+
const buildSearchParams = (query) => {
|
|
201
|
+
const searchParams = new URLSearchParams();
|
|
202
|
+
for (const [key, value] of Object.entries(query)) {
|
|
203
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
204
|
+
Object.entries(value)
|
|
205
|
+
.filter((entry) => entry[1] ?? false)
|
|
206
|
+
.forEach((entry) => searchParams.set(`${key}[${entry[0]}]`, String(entry[1])));
|
|
207
|
+
}
|
|
208
|
+
else if (Array.isArray(value)) {
|
|
209
|
+
value.forEach((val) => {
|
|
210
|
+
searchParams.append(`${key}[]`, val);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
else if (typeof value === 'string' && value) {
|
|
214
|
+
searchParams.set(key, value);
|
|
215
|
+
}
|
|
216
|
+
else if (typeof value === 'number') {
|
|
217
|
+
searchParams.set(key, value.toString());
|
|
218
|
+
}
|
|
212
219
|
}
|
|
213
|
-
|
|
214
|
-
|
|
220
|
+
return searchParams.toString();
|
|
221
|
+
};
|
|
222
|
+
const getUrl = (base, path, query) => {
|
|
223
|
+
const url = new URL(base);
|
|
224
|
+
url.pathname = path;
|
|
225
|
+
if (query) {
|
|
226
|
+
url.search = buildSearchParams(query);
|
|
215
227
|
}
|
|
216
|
-
return
|
|
217
|
-
}
|
|
218
|
-
.filter((x) => !!x)
|
|
219
|
-
.join('&');
|
|
220
|
-
const getUrl = (base, path, query) => [
|
|
221
|
-
base,
|
|
222
|
-
path,
|
|
223
|
-
query && Object.keys(query).length > 0 ? '?' : '',
|
|
224
|
-
query && createQuery(query)
|
|
225
|
-
]
|
|
226
|
-
.filter(Boolean)
|
|
227
|
-
.join('');
|
|
228
|
+
return url.toString();
|
|
229
|
+
};
|
|
228
230
|
|
|
229
231
|
/*
|
|
230
232
|
Settings for future support:
|
|
@@ -246,14 +248,59 @@ const defaultSettings = {
|
|
|
246
248
|
const defaultContentType = 'application/octet-stream';
|
|
247
249
|
const defaultFilename = 'original';
|
|
248
250
|
|
|
249
|
-
var version = '4.
|
|
251
|
+
var version = '4.2.1';
|
|
252
|
+
|
|
253
|
+
function isObject(o) {
|
|
254
|
+
return Object.prototype.toString.call(o) === '[object Object]';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const SEPARATOR = /\W|_/g;
|
|
258
|
+
function camelizeString(text) {
|
|
259
|
+
return text
|
|
260
|
+
.split(SEPARATOR)
|
|
261
|
+
.map((word, index) => word.charAt(0)[index > 0 ? 'toUpperCase' : 'toLowerCase']() +
|
|
262
|
+
word.slice(1))
|
|
263
|
+
.join('');
|
|
264
|
+
}
|
|
265
|
+
function camelizeArrayItems(array, { ignoreKeys } = { ignoreKeys: [] }) {
|
|
266
|
+
if (!Array.isArray(array)) {
|
|
267
|
+
return array;
|
|
268
|
+
}
|
|
269
|
+
return array.map((item) => camelizeKeys(item, { ignoreKeys }));
|
|
270
|
+
}
|
|
271
|
+
function camelizeKeys(source, { ignoreKeys } = { ignoreKeys: [] }) {
|
|
272
|
+
if (Array.isArray(source)) {
|
|
273
|
+
return camelizeArrayItems(source, { ignoreKeys });
|
|
274
|
+
}
|
|
275
|
+
if (!isObject(source)) {
|
|
276
|
+
return source;
|
|
277
|
+
}
|
|
278
|
+
const result = {};
|
|
279
|
+
for (const key of Object.keys(source)) {
|
|
280
|
+
let value = source[key];
|
|
281
|
+
if (ignoreKeys.includes(key)) {
|
|
282
|
+
result[key] = value;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (isObject(value)) {
|
|
286
|
+
value = camelizeKeys(value, { ignoreKeys });
|
|
287
|
+
}
|
|
288
|
+
else if (Array.isArray(value)) {
|
|
289
|
+
value = camelizeArrayItems(value, { ignoreKeys });
|
|
290
|
+
}
|
|
291
|
+
result[camelizeString(key)] = value;
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
250
295
|
|
|
251
296
|
/**
|
|
252
|
-
*
|
|
297
|
+
* setTimeout as Promise.
|
|
298
|
+
*
|
|
299
|
+
* @param {number} ms Timeout in milliseconds.
|
|
253
300
|
*/
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
301
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
302
|
+
|
|
303
|
+
function getUserAgent$1({ libraryName, libraryVersion, userAgent, publicKey = '', integration = '' }) {
|
|
257
304
|
const languageName = 'JavaScript';
|
|
258
305
|
if (typeof userAgent === 'string') {
|
|
259
306
|
return userAgent;
|
|
@@ -274,39 +321,6 @@ function getUserAgent({ userAgent, publicKey = '', integration = '' } = {}) {
|
|
|
274
321
|
return `${mainInfo} (${additionInfo})`;
|
|
275
322
|
}
|
|
276
323
|
|
|
277
|
-
const SEPARATOR = /\W|_/g;
|
|
278
|
-
/**
|
|
279
|
-
* Transforms a string to camelCased.
|
|
280
|
-
*/
|
|
281
|
-
function camelize(text) {
|
|
282
|
-
return text
|
|
283
|
-
.split(SEPARATOR)
|
|
284
|
-
.map((word, index) => word.charAt(0)[index > 0 ? 'toUpperCase' : 'toLowerCase']() +
|
|
285
|
-
word.slice(1))
|
|
286
|
-
.join('');
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Transforms keys of an object to camelCased recursively.
|
|
290
|
-
*/
|
|
291
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
292
|
-
function camelizeKeys(source) {
|
|
293
|
-
if (!source || typeof source !== 'object') {
|
|
294
|
-
return source;
|
|
295
|
-
}
|
|
296
|
-
return Object.keys(source).reduce((accumulator, key) => {
|
|
297
|
-
accumulator[camelize(key)] =
|
|
298
|
-
typeof source[key] === 'object' ? camelizeKeys(source[key]) : source[key];
|
|
299
|
-
return accumulator;
|
|
300
|
-
}, {});
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* setTimeout as Promise.
|
|
305
|
-
*
|
|
306
|
-
* @param {number} ms Timeout in milliseconds.
|
|
307
|
-
*/
|
|
308
|
-
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
309
|
-
|
|
310
324
|
const defaultOptions = {
|
|
311
325
|
factor: 2,
|
|
312
326
|
time: 100
|
|
@@ -314,8 +328,8 @@ const defaultOptions = {
|
|
|
314
328
|
function retrier(fn, options = defaultOptions) {
|
|
315
329
|
let attempts = 0;
|
|
316
330
|
function runAttempt(fn) {
|
|
317
|
-
const defaultDelayTime = Math.round(options.time *
|
|
318
|
-
const retry = (ms) => delay(ms
|
|
331
|
+
const defaultDelayTime = Math.round(options.time * options.factor ** attempts);
|
|
332
|
+
const retry = (ms) => delay(ms ?? defaultDelayTime).then(() => {
|
|
319
333
|
attempts += 1;
|
|
320
334
|
return runAttempt(fn);
|
|
321
335
|
});
|
|
@@ -327,6 +341,16 @@ function retrier(fn, options = defaultOptions) {
|
|
|
327
341
|
return runAttempt(fn);
|
|
328
342
|
}
|
|
329
343
|
|
|
344
|
+
const LIBRARY_NAME = 'UploadcareUploadClient';
|
|
345
|
+
const LIBRARY_VERSION = version;
|
|
346
|
+
function getUserAgent(options) {
|
|
347
|
+
return getUserAgent$1({
|
|
348
|
+
libraryName: LIBRARY_NAME,
|
|
349
|
+
libraryVersion: LIBRARY_VERSION,
|
|
350
|
+
...options
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
330
354
|
const REQUEST_WAS_THROTTLED_CODE = 'RequestThrottledError';
|
|
331
355
|
const DEFAULT_RETRY_AFTER_TIMEOUT = 15000;
|
|
332
356
|
function getTimeoutFromThrottledRequest(error) {
|
|
@@ -338,7 +362,7 @@ function getTimeoutFromThrottledRequest(error) {
|
|
|
338
362
|
function retryIfThrottled(fn, retryThrottledMaxTimes) {
|
|
339
363
|
return retrier(({ attempt, retry }) => fn().catch((error) => {
|
|
340
364
|
if ('response' in error &&
|
|
341
|
-
|
|
365
|
+
error?.code === REQUEST_WAS_THROTTLED_CODE &&
|
|
342
366
|
attempt < retryThrottledMaxTimes) {
|
|
343
367
|
return retry(getTimeoutFromThrottledRequest(error));
|
|
344
368
|
}
|
|
@@ -355,41 +379,38 @@ function getStoreValue(store) {
|
|
|
355
379
|
* Can be canceled and has progress.
|
|
356
380
|
*/
|
|
357
381
|
function base(file, { publicKey, fileName, contentType, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
|
|
358
|
-
return retryIfThrottled(() => {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
|
|
382
|
+
return retryIfThrottled(() => request({
|
|
383
|
+
method: 'POST',
|
|
384
|
+
url: getUrl(baseURL, '/base/', {
|
|
385
|
+
jsonerrors: 1
|
|
386
|
+
}),
|
|
387
|
+
headers: {
|
|
388
|
+
'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
|
|
389
|
+
},
|
|
390
|
+
data: buildFormData({
|
|
391
|
+
file: {
|
|
392
|
+
data: file,
|
|
393
|
+
name: fileName ?? file.name ?? defaultFilename,
|
|
394
|
+
contentType
|
|
367
395
|
},
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
|
|
387
|
-
}
|
|
388
|
-
else {
|
|
389
|
-
return response;
|
|
390
|
-
}
|
|
391
|
-
});
|
|
392
|
-
}, retryThrottledRequestMaxTimes);
|
|
396
|
+
UPLOADCARE_PUB_KEY: publicKey,
|
|
397
|
+
UPLOADCARE_STORE: getStoreValue(store),
|
|
398
|
+
signature: secureSignature,
|
|
399
|
+
expire: secureExpire,
|
|
400
|
+
source: source,
|
|
401
|
+
metadata
|
|
402
|
+
}),
|
|
403
|
+
signal,
|
|
404
|
+
onProgress
|
|
405
|
+
}).then(({ data, headers, request }) => {
|
|
406
|
+
const response = camelizeKeys(JSON.parse(data));
|
|
407
|
+
if ('error' in response) {
|
|
408
|
+
throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
return response;
|
|
412
|
+
}
|
|
413
|
+
}), retryThrottledRequestMaxTimes);
|
|
393
414
|
}
|
|
394
415
|
|
|
395
416
|
var TypeEnum;
|
|
@@ -568,9 +589,9 @@ function multipartStart(size, { publicKey, contentType, fileName, multipartChunk
|
|
|
568
589
|
'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
|
|
569
590
|
},
|
|
570
591
|
data: buildFormData({
|
|
571
|
-
filename: fileName
|
|
592
|
+
filename: fileName ?? defaultFilename,
|
|
572
593
|
size: size,
|
|
573
|
-
content_type: contentType
|
|
594
|
+
content_type: contentType ?? defaultContentType,
|
|
574
595
|
part_size: multipartChunkSize,
|
|
575
596
|
UPLOADCARE_STORE: getStoreValue(store),
|
|
576
597
|
UPLOADCARE_PUB_KEY: publicKey,
|
|
@@ -672,9 +693,9 @@ class UploadcareFile {
|
|
|
672
693
|
this.mimeType = fileInfo.mimeType;
|
|
673
694
|
this.cdnUrl = cdnUrl;
|
|
674
695
|
this.originalFilename = fileInfo.originalFilename;
|
|
675
|
-
this.imageInfo =
|
|
676
|
-
this.videoInfo =
|
|
677
|
-
this.contentInfo =
|
|
696
|
+
this.imageInfo = fileInfo.imageInfo;
|
|
697
|
+
this.videoInfo = fileInfo.videoInfo;
|
|
698
|
+
this.contentInfo = fileInfo.contentInfo;
|
|
678
699
|
this.metadata = fileInfo.metadata || null;
|
|
679
700
|
this.s3Bucket = s3Bucket || null;
|
|
680
701
|
this.s3Url = s3Url;
|
|
@@ -802,8 +823,7 @@ class Events {
|
|
|
802
823
|
this.events = Object.create({});
|
|
803
824
|
}
|
|
804
825
|
emit(event, data) {
|
|
805
|
-
|
|
806
|
-
(_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.forEach((fn) => fn(data));
|
|
826
|
+
this.events[event]?.forEach((fn) => fn(data));
|
|
807
827
|
}
|
|
808
828
|
on(event, callback) {
|
|
809
829
|
this.events[event] = this.events[event] || [];
|
|
@@ -821,12 +841,12 @@ class Events {
|
|
|
821
841
|
|
|
822
842
|
const response = (type, data) => {
|
|
823
843
|
if (type === 'success') {
|
|
824
|
-
return
|
|
844
|
+
return { status: Status.Success, ...data };
|
|
825
845
|
}
|
|
826
846
|
if (type === 'progress') {
|
|
827
|
-
return
|
|
847
|
+
return { status: Status.Progress, ...data };
|
|
828
848
|
}
|
|
829
|
-
return
|
|
849
|
+
return { status: Status.Error, ...data };
|
|
830
850
|
};
|
|
831
851
|
class Pusher {
|
|
832
852
|
constructor(pusherKey, disconnectTime = 30000) {
|
|
@@ -874,8 +894,7 @@ class Pusher {
|
|
|
874
894
|
}
|
|
875
895
|
disconnect() {
|
|
876
896
|
const actualDisconect = () => {
|
|
877
|
-
|
|
878
|
-
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
|
|
897
|
+
this.ws?.close();
|
|
879
898
|
this.ws = undefined;
|
|
880
899
|
this.isConnected = false;
|
|
881
900
|
};
|
|
@@ -889,9 +908,8 @@ class Pusher {
|
|
|
889
908
|
}
|
|
890
909
|
}
|
|
891
910
|
send(event, data) {
|
|
892
|
-
var _a;
|
|
893
911
|
const str = JSON.stringify({ event, data });
|
|
894
|
-
|
|
912
|
+
this.ws?.send(str);
|
|
895
913
|
}
|
|
896
914
|
subscribe(token, handler) {
|
|
897
915
|
this.subscribers += 1;
|
|
@@ -1058,7 +1076,7 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
|
|
|
1058
1076
|
}))
|
|
1059
1077
|
.catch((error) => {
|
|
1060
1078
|
const pusher = getPusher(pusherKey);
|
|
1061
|
-
pusher
|
|
1079
|
+
pusher?.disconnect();
|
|
1062
1080
|
return Promise.reject(error);
|
|
1063
1081
|
})
|
|
1064
1082
|
.then((urlResponse) => {
|
|
@@ -1218,7 +1236,7 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
|
|
|
1218
1236
|
return multipartStart(size, {
|
|
1219
1237
|
publicKey,
|
|
1220
1238
|
contentType,
|
|
1221
|
-
fileName: fileName
|
|
1239
|
+
fileName: fileName ?? file.name,
|
|
1222
1240
|
baseURL,
|
|
1223
1241
|
secureSignature,
|
|
1224
1242
|
secureExpire,
|
|
@@ -1470,7 +1488,10 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
|
|
|
1470
1488
|
/**
|
|
1471
1489
|
* Populate options with settings.
|
|
1472
1490
|
*/
|
|
1473
|
-
const populateOptionsWithSettings = (options, settings) => (
|
|
1491
|
+
const populateOptionsWithSettings = (options, settings) => ({
|
|
1492
|
+
...settings,
|
|
1493
|
+
...options
|
|
1494
|
+
});
|
|
1474
1495
|
class UploadClient {
|
|
1475
1496
|
constructor(settings) {
|
|
1476
1497
|
this.settings = Object.assign({}, defaultSettings, settings);
|