@uploadcare/upload-client 4.0.1 → 4.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.
@@ -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 = (method === null || method === void 0 ? void 0 : method.toUpperCase()) || 'GET';
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 serializePair = (key, value) => typeof value !== 'undefined' ? `${key}=${encodeURIComponent(value)}` : null;
201
- // TODO: generalize value transforming logic and use it here and inside `buildFormData`
202
- const createQuery = (query) => Object.entries(query)
203
- .reduce((params, [key, value]) => {
204
- let param;
205
- if (typeof value === 'object' && !Array.isArray(value)) {
206
- param = Object.entries(value)
207
- .filter((entry) => typeof entry[1] !== 'undefined')
208
- .map((entry) => serializePair(`${key}[${entry[0]}]`, String(entry[1])));
209
- }
210
- else if (Array.isArray(value)) {
211
- param = value.map((val) => serializePair(`${key}[]`, val));
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
- else {
214
- param = serializePair(key, value);
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 params.concat(param);
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,7 +248,7 @@ const defaultSettings = {
246
248
  const defaultContentType = 'application/octet-stream';
247
249
  const defaultFilename = 'original';
248
250
 
249
- var version = '4.0.1';
251
+ var version = '4.1.0';
250
252
 
251
253
  /**
252
254
  * Returns User Agent based on version and settings.
@@ -274,6 +276,13 @@ function getUserAgent({ userAgent, publicKey = '', integration = '' } = {}) {
274
276
  return `${mainInfo} (${additionInfo})`;
275
277
  }
276
278
 
279
+ /**
280
+ * setTimeout as Promise.
281
+ *
282
+ * @param {number} ms Timeout in milliseconds.
283
+ */
284
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
285
+
277
286
  const SEPARATOR = /\W|_/g;
278
287
  /**
279
288
  * Transforms a string to camelCased.
@@ -300,13 +309,6 @@ function camelizeKeys(source) {
300
309
  }, {});
301
310
  }
302
311
 
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
312
  const defaultOptions = {
311
313
  factor: 2,
312
314
  time: 100
@@ -314,8 +316,8 @@ const defaultOptions = {
314
316
  function retrier(fn, options = defaultOptions) {
315
317
  let attempts = 0;
316
318
  function runAttempt(fn) {
317
- const defaultDelayTime = Math.round(options.time * Math.pow(options.factor, attempts));
318
- const retry = (ms) => delay(ms !== null && ms !== void 0 ? ms : defaultDelayTime).then(() => {
319
+ const defaultDelayTime = Math.round(options.time * options.factor ** attempts);
320
+ const retry = (ms) => delay(ms ?? defaultDelayTime).then(() => {
319
321
  attempts += 1;
320
322
  return runAttempt(fn);
321
323
  });
@@ -338,7 +340,7 @@ function getTimeoutFromThrottledRequest(error) {
338
340
  function retryIfThrottled(fn, retryThrottledMaxTimes) {
339
341
  return retrier(({ attempt, retry }) => fn().catch((error) => {
340
342
  if ('response' in error &&
341
- (error === null || error === void 0 ? void 0 : error.code) === REQUEST_WAS_THROTTLED_CODE &&
343
+ error?.code === REQUEST_WAS_THROTTLED_CODE &&
342
344
  attempt < retryThrottledMaxTimes) {
343
345
  return retry(getTimeoutFromThrottledRequest(error));
344
346
  }
@@ -355,41 +357,38 @@ function getStoreValue(store) {
355
357
  * Can be canceled and has progress.
356
358
  */
357
359
  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
- var _a;
360
- return request({
361
- method: 'POST',
362
- url: getUrl(baseURL, '/base/', {
363
- jsonerrors: 1
364
- }),
365
- headers: {
366
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
360
+ return retryIfThrottled(() => request({
361
+ method: 'POST',
362
+ url: getUrl(baseURL, '/base/', {
363
+ jsonerrors: 1
364
+ }),
365
+ headers: {
366
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
367
+ },
368
+ data: buildFormData({
369
+ file: {
370
+ data: file,
371
+ name: fileName ?? file.name ?? defaultFilename,
372
+ contentType
367
373
  },
368
- data: buildFormData({
369
- file: {
370
- data: file,
371
- name: (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename,
372
- contentType
373
- },
374
- UPLOADCARE_PUB_KEY: publicKey,
375
- UPLOADCARE_STORE: getStoreValue(store),
376
- signature: secureSignature,
377
- expire: secureExpire,
378
- source: source,
379
- metadata
380
- }),
381
- signal,
382
- onProgress
383
- }).then(({ data, headers, request }) => {
384
- const response = camelizeKeys(JSON.parse(data));
385
- if ('error' in response) {
386
- throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
387
- }
388
- else {
389
- return response;
390
- }
391
- });
392
- }, retryThrottledRequestMaxTimes);
374
+ UPLOADCARE_PUB_KEY: publicKey,
375
+ UPLOADCARE_STORE: getStoreValue(store),
376
+ signature: secureSignature,
377
+ expire: secureExpire,
378
+ source: source,
379
+ metadata
380
+ }),
381
+ signal,
382
+ onProgress
383
+ }).then(({ data, headers, request }) => {
384
+ const response = camelizeKeys(JSON.parse(data));
385
+ if ('error' in response) {
386
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
387
+ }
388
+ else {
389
+ return response;
390
+ }
391
+ }), retryThrottledRequestMaxTimes);
393
392
  }
394
393
 
395
394
  var TypeEnum;
@@ -568,9 +567,9 @@ function multipartStart(size, { publicKey, contentType, fileName, multipartChunk
568
567
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
569
568
  },
570
569
  data: buildFormData({
571
- filename: fileName !== null && fileName !== void 0 ? fileName : defaultFilename,
570
+ filename: fileName ?? defaultFilename,
572
571
  size: size,
573
- content_type: contentType !== null && contentType !== void 0 ? contentType : defaultContentType,
572
+ content_type: contentType ?? defaultContentType,
574
573
  part_size: multipartChunkSize,
575
574
  UPLOADCARE_STORE: getStoreValue(store),
576
575
  UPLOADCARE_PUB_KEY: publicKey,
@@ -802,8 +801,7 @@ class Events {
802
801
  this.events = Object.create({});
803
802
  }
804
803
  emit(event, data) {
805
- var _a;
806
- (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.forEach((fn) => fn(data));
804
+ this.events[event]?.forEach((fn) => fn(data));
807
805
  }
808
806
  on(event, callback) {
809
807
  this.events[event] = this.events[event] || [];
@@ -821,12 +819,12 @@ class Events {
821
819
 
822
820
  const response = (type, data) => {
823
821
  if (type === 'success') {
824
- return Object.assign({ status: Status.Success }, data);
822
+ return { status: Status.Success, ...data };
825
823
  }
826
824
  if (type === 'progress') {
827
- return Object.assign({ status: Status.Progress }, data);
825
+ return { status: Status.Progress, ...data };
828
826
  }
829
- return Object.assign({ status: Status.Error }, data);
827
+ return { status: Status.Error, ...data };
830
828
  };
831
829
  class Pusher {
832
830
  constructor(pusherKey, disconnectTime = 30000) {
@@ -874,8 +872,7 @@ class Pusher {
874
872
  }
875
873
  disconnect() {
876
874
  const actualDisconect = () => {
877
- var _a;
878
- (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
875
+ this.ws?.close();
879
876
  this.ws = undefined;
880
877
  this.isConnected = false;
881
878
  };
@@ -889,9 +886,8 @@ class Pusher {
889
886
  }
890
887
  }
891
888
  send(event, data) {
892
- var _a;
893
889
  const str = JSON.stringify({ event, data });
894
- (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(str);
890
+ this.ws?.send(str);
895
891
  }
896
892
  subscribe(token, handler) {
897
893
  this.subscribers += 1;
@@ -1058,7 +1054,7 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
1058
1054
  }))
1059
1055
  .catch((error) => {
1060
1056
  const pusher = getPusher(pusherKey);
1061
- pusher === null || pusher === void 0 ? void 0 : pusher.disconnect();
1057
+ pusher?.disconnect();
1062
1058
  return Promise.reject(error);
1063
1059
  })
1064
1060
  .then((urlResponse) => {
@@ -1218,7 +1214,7 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1218
1214
  return multipartStart(size, {
1219
1215
  publicKey,
1220
1216
  contentType,
1221
- fileName: fileName !== null && fileName !== void 0 ? fileName : file.name,
1217
+ fileName: fileName ?? file.name,
1222
1218
  baseURL,
1223
1219
  secureSignature,
1224
1220
  secureExpire,
@@ -1470,7 +1466,10 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1470
1466
  /**
1471
1467
  * Populate options with settings.
1472
1468
  */
1473
- const populateOptionsWithSettings = (options, settings) => (Object.assign(Object.assign({}, settings), options));
1469
+ const populateOptionsWithSettings = (options, settings) => ({
1470
+ ...settings,
1471
+ ...options
1472
+ });
1474
1473
  class UploadClient {
1475
1474
  constructor(settings) {
1476
1475
  this.settings = Object.assign({}, defaultSettings, settings);
@@ -150,7 +150,7 @@ const getFileOptions = ({ name, contentType }) => [
150
150
  contentType
151
151
  })
152
152
  .filter(([, value]) => !!value)
153
- .reduce((acc, [key, value]) => (Object.assign(Object.assign({}, acc), { [key]: value })), {})
153
+ .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
154
154
  ].filter((value) => !!value);
155
155
  const transformFile = identity;
156
156
  var getFormData = () => new NodeFormData();
@@ -231,34 +231,36 @@ function buildFormData(options) {
231
231
  return formData;
232
232
  }
233
233
 
234
- const serializePair = (key, value) => typeof value !== 'undefined' ? `${key}=${encodeURIComponent(value)}` : null;
235
- // TODO: generalize value transforming logic and use it here and inside `buildFormData`
236
- const createQuery = (query) => Object.entries(query)
237
- .reduce((params, [key, value]) => {
238
- let param;
239
- if (typeof value === 'object' && !Array.isArray(value)) {
240
- param = Object.entries(value)
241
- .filter((entry) => typeof entry[1] !== 'undefined')
242
- .map((entry) => serializePair(`${key}[${entry[0]}]`, String(entry[1])));
243
- }
244
- else if (Array.isArray(value)) {
245
- param = value.map((val) => serializePair(`${key}[]`, val));
234
+ const buildSearchParams = (query) => {
235
+ const searchParams = new URLSearchParams();
236
+ for (const [key, value] of Object.entries(query)) {
237
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
238
+ Object.entries(value)
239
+ .filter((entry) => entry[1] ?? false)
240
+ .forEach((entry) => searchParams.set(`${key}[${entry[0]}]`, String(entry[1])));
241
+ }
242
+ else if (Array.isArray(value)) {
243
+ value.forEach((val) => {
244
+ searchParams.append(`${key}[]`, val);
245
+ });
246
+ }
247
+ else if (typeof value === 'string' && value) {
248
+ searchParams.set(key, value);
249
+ }
250
+ else if (typeof value === 'number') {
251
+ searchParams.set(key, value.toString());
252
+ }
246
253
  }
247
- else {
248
- param = serializePair(key, value);
254
+ return searchParams.toString();
255
+ };
256
+ const getUrl = (base, path, query) => {
257
+ const url = new URL(base);
258
+ url.pathname = path;
259
+ if (query) {
260
+ url.search = buildSearchParams(query);
249
261
  }
250
- return params.concat(param);
251
- }, [])
252
- .filter((x) => !!x)
253
- .join('&');
254
- const getUrl = (base, path, query) => [
255
- base,
256
- path,
257
- query && Object.keys(query).length > 0 ? '?' : '',
258
- query && createQuery(query)
259
- ]
260
- .filter(Boolean)
261
- .join('');
262
+ return url.toString();
263
+ };
262
264
 
263
265
  /*
264
266
  Settings for future support:
@@ -280,7 +282,7 @@ const defaultSettings = {
280
282
  const defaultContentType = 'application/octet-stream';
281
283
  const defaultFilename = 'original';
282
284
 
283
- var version = '4.0.1';
285
+ var version = '4.1.0';
284
286
 
285
287
  /**
286
288
  * Returns User Agent based on version and settings.
@@ -308,6 +310,13 @@ function getUserAgent({ userAgent, publicKey = '', integration = '' } = {}) {
308
310
  return `${mainInfo} (${additionInfo})`;
309
311
  }
310
312
 
313
+ /**
314
+ * setTimeout as Promise.
315
+ *
316
+ * @param {number} ms Timeout in milliseconds.
317
+ */
318
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
319
+
311
320
  const SEPARATOR = /\W|_/g;
312
321
  /**
313
322
  * Transforms a string to camelCased.
@@ -334,13 +343,6 @@ function camelizeKeys(source) {
334
343
  }, {});
335
344
  }
336
345
 
337
- /**
338
- * setTimeout as Promise.
339
- *
340
- * @param {number} ms Timeout in milliseconds.
341
- */
342
- const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
343
-
344
346
  const defaultOptions = {
345
347
  factor: 2,
346
348
  time: 100
@@ -348,8 +350,8 @@ const defaultOptions = {
348
350
  function retrier(fn, options = defaultOptions) {
349
351
  let attempts = 0;
350
352
  function runAttempt(fn) {
351
- const defaultDelayTime = Math.round(options.time * Math.pow(options.factor, attempts));
352
- const retry = (ms) => delay(ms !== null && ms !== void 0 ? ms : defaultDelayTime).then(() => {
353
+ const defaultDelayTime = Math.round(options.time * options.factor ** attempts);
354
+ const retry = (ms) => delay(ms ?? defaultDelayTime).then(() => {
353
355
  attempts += 1;
354
356
  return runAttempt(fn);
355
357
  });
@@ -372,7 +374,7 @@ function getTimeoutFromThrottledRequest(error) {
372
374
  function retryIfThrottled(fn, retryThrottledMaxTimes) {
373
375
  return retrier(({ attempt, retry }) => fn().catch((error) => {
374
376
  if ('response' in error &&
375
- (error === null || error === void 0 ? void 0 : error.code) === REQUEST_WAS_THROTTLED_CODE &&
377
+ error?.code === REQUEST_WAS_THROTTLED_CODE &&
376
378
  attempt < retryThrottledMaxTimes) {
377
379
  return retry(getTimeoutFromThrottledRequest(error));
378
380
  }
@@ -389,41 +391,38 @@ function getStoreValue(store) {
389
391
  * Can be canceled and has progress.
390
392
  */
391
393
  function base(file, { publicKey, fileName, contentType, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
392
- return retryIfThrottled(() => {
393
- var _a;
394
- return request({
395
- method: 'POST',
396
- url: getUrl(baseURL, '/base/', {
397
- jsonerrors: 1
398
- }),
399
- headers: {
400
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
394
+ return retryIfThrottled(() => request({
395
+ method: 'POST',
396
+ url: getUrl(baseURL, '/base/', {
397
+ jsonerrors: 1
398
+ }),
399
+ headers: {
400
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
401
+ },
402
+ data: buildFormData({
403
+ file: {
404
+ data: file,
405
+ name: fileName ?? file.name ?? defaultFilename,
406
+ contentType
401
407
  },
402
- data: buildFormData({
403
- file: {
404
- data: file,
405
- name: (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename,
406
- contentType
407
- },
408
- UPLOADCARE_PUB_KEY: publicKey,
409
- UPLOADCARE_STORE: getStoreValue(store),
410
- signature: secureSignature,
411
- expire: secureExpire,
412
- source: source,
413
- metadata
414
- }),
415
- signal,
416
- onProgress
417
- }).then(({ data, headers, request }) => {
418
- const response = camelizeKeys(JSON.parse(data));
419
- if ('error' in response) {
420
- throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
421
- }
422
- else {
423
- return response;
424
- }
425
- });
426
- }, retryThrottledRequestMaxTimes);
408
+ UPLOADCARE_PUB_KEY: publicKey,
409
+ UPLOADCARE_STORE: getStoreValue(store),
410
+ signature: secureSignature,
411
+ expire: secureExpire,
412
+ source: source,
413
+ metadata
414
+ }),
415
+ signal,
416
+ onProgress
417
+ }).then(({ data, headers, request }) => {
418
+ const response = camelizeKeys(JSON.parse(data));
419
+ if ('error' in response) {
420
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
421
+ }
422
+ else {
423
+ return response;
424
+ }
425
+ }), retryThrottledRequestMaxTimes);
427
426
  }
428
427
 
429
428
  var TypeEnum;
@@ -602,9 +601,9 @@ function multipartStart(size, { publicKey, contentType, fileName, multipartChunk
602
601
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
603
602
  },
604
603
  data: buildFormData({
605
- filename: fileName !== null && fileName !== void 0 ? fileName : defaultFilename,
604
+ filename: fileName ?? defaultFilename,
606
605
  size: size,
607
- content_type: contentType !== null && contentType !== void 0 ? contentType : defaultContentType,
606
+ content_type: contentType ?? defaultContentType,
608
607
  part_size: multipartChunkSize,
609
608
  UPLOADCARE_STORE: getStoreValue(store),
610
609
  UPLOADCARE_PUB_KEY: publicKey,
@@ -834,8 +833,7 @@ class Events {
834
833
  this.events = Object.create({});
835
834
  }
836
835
  emit(event, data) {
837
- var _a;
838
- (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.forEach((fn) => fn(data));
836
+ this.events[event]?.forEach((fn) => fn(data));
839
837
  }
840
838
  on(event, callback) {
841
839
  this.events[event] = this.events[event] || [];
@@ -853,12 +851,12 @@ class Events {
853
851
 
854
852
  const response = (type, data) => {
855
853
  if (type === 'success') {
856
- return Object.assign({ status: Status.Success }, data);
854
+ return { status: Status.Success, ...data };
857
855
  }
858
856
  if (type === 'progress') {
859
- return Object.assign({ status: Status.Progress }, data);
857
+ return { status: Status.Progress, ...data };
860
858
  }
861
- return Object.assign({ status: Status.Error }, data);
859
+ return { status: Status.Error, ...data };
862
860
  };
863
861
  class Pusher {
864
862
  constructor(pusherKey, disconnectTime = 30000) {
@@ -906,8 +904,7 @@ class Pusher {
906
904
  }
907
905
  disconnect() {
908
906
  const actualDisconect = () => {
909
- var _a;
910
- (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
907
+ this.ws?.close();
911
908
  this.ws = undefined;
912
909
  this.isConnected = false;
913
910
  };
@@ -921,9 +918,8 @@ class Pusher {
921
918
  }
922
919
  }
923
920
  send(event, data) {
924
- var _a;
925
921
  const str = JSON.stringify({ event, data });
926
- (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(str);
922
+ this.ws?.send(str);
927
923
  }
928
924
  subscribe(token, handler) {
929
925
  this.subscribers += 1;
@@ -1090,7 +1086,7 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
1090
1086
  }))
1091
1087
  .catch((error) => {
1092
1088
  const pusher = getPusher(pusherKey);
1093
- pusher === null || pusher === void 0 ? void 0 : pusher.disconnect();
1089
+ pusher?.disconnect();
1094
1090
  return Promise.reject(error);
1095
1091
  })
1096
1092
  .then((urlResponse) => {
@@ -1250,7 +1246,7 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1250
1246
  return multipartStart(size, {
1251
1247
  publicKey,
1252
1248
  contentType,
1253
- fileName: fileName !== null && fileName !== void 0 ? fileName : file.name,
1249
+ fileName: fileName ?? file.name,
1254
1250
  baseURL,
1255
1251
  secureSignature,
1256
1252
  secureExpire,
@@ -1502,7 +1498,10 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1502
1498
  /**
1503
1499
  * Populate options with settings.
1504
1500
  */
1505
- const populateOptionsWithSettings = (options, settings) => (Object.assign(Object.assign({}, settings), options));
1501
+ const populateOptionsWithSettings = (options, settings) => ({
1502
+ ...settings,
1503
+ ...options
1504
+ });
1506
1505
  class UploadClient {
1507
1506
  constructor(settings) {
1508
1507
  this.settings = Object.assign({}, defaultSettings, settings);
@@ -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 = (method === null || method === void 0 ? void 0 : method.toUpperCase()) || 'GET';
32
+ const requestMethod = method?.toUpperCase() || 'GET';
33
33
  let aborted = false;
34
34
  xhr.open(requestMethod, url);
35
35
  if (headers) {
@@ -200,34 +200,36 @@ function buildFormData(options) {
200
200
  return formData;
201
201
  }
202
202
 
203
- const serializePair = (key, value) => typeof value !== 'undefined' ? `${key}=${encodeURIComponent(value)}` : null;
204
- // TODO: generalize value transforming logic and use it here and inside `buildFormData`
205
- const createQuery = (query) => Object.entries(query)
206
- .reduce((params, [key, value]) => {
207
- let param;
208
- if (typeof value === 'object' && !Array.isArray(value)) {
209
- param = Object.entries(value)
210
- .filter((entry) => typeof entry[1] !== 'undefined')
211
- .map((entry) => serializePair(`${key}[${entry[0]}]`, String(entry[1])));
212
- }
213
- else if (Array.isArray(value)) {
214
- param = value.map((val) => serializePair(`${key}[]`, val));
203
+ const buildSearchParams = (query) => {
204
+ const searchParams = new URLSearchParams();
205
+ for (const [key, value] of Object.entries(query)) {
206
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
207
+ Object.entries(value)
208
+ .filter((entry) => entry[1] ?? false)
209
+ .forEach((entry) => searchParams.set(`${key}[${entry[0]}]`, String(entry[1])));
210
+ }
211
+ else if (Array.isArray(value)) {
212
+ value.forEach((val) => {
213
+ searchParams.append(`${key}[]`, val);
214
+ });
215
+ }
216
+ else if (typeof value === 'string' && value) {
217
+ searchParams.set(key, value);
218
+ }
219
+ else if (typeof value === 'number') {
220
+ searchParams.set(key, value.toString());
221
+ }
215
222
  }
216
- else {
217
- param = serializePair(key, value);
223
+ return searchParams.toString();
224
+ };
225
+ const getUrl = (base, path, query) => {
226
+ const url = new URL(base);
227
+ url.pathname = path;
228
+ if (query) {
229
+ url.search = buildSearchParams(query);
218
230
  }
219
- return params.concat(param);
220
- }, [])
221
- .filter((x) => !!x)
222
- .join('&');
223
- const getUrl = (base, path, query) => [
224
- base,
225
- path,
226
- query && Object.keys(query).length > 0 ? '?' : '',
227
- query && createQuery(query)
228
- ]
229
- .filter(Boolean)
230
- .join('');
231
+ return url.toString();
232
+ };
231
233
 
232
234
  /*
233
235
  Settings for future support:
@@ -249,7 +251,7 @@ const defaultSettings = {
249
251
  const defaultContentType = 'application/octet-stream';
250
252
  const defaultFilename = 'original';
251
253
 
252
- var version = '4.0.1';
254
+ var version = '4.1.0';
253
255
 
254
256
  /**
255
257
  * Returns User Agent based on version and settings.
@@ -277,6 +279,13 @@ function getUserAgent({ userAgent, publicKey = '', integration = '' } = {}) {
277
279
  return `${mainInfo} (${additionInfo})`;
278
280
  }
279
281
 
282
+ /**
283
+ * setTimeout as Promise.
284
+ *
285
+ * @param {number} ms Timeout in milliseconds.
286
+ */
287
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
288
+
280
289
  const SEPARATOR = /\W|_/g;
281
290
  /**
282
291
  * Transforms a string to camelCased.
@@ -303,13 +312,6 @@ function camelizeKeys(source) {
303
312
  }, {});
304
313
  }
305
314
 
306
- /**
307
- * setTimeout as Promise.
308
- *
309
- * @param {number} ms Timeout in milliseconds.
310
- */
311
- const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
312
-
313
315
  const defaultOptions = {
314
316
  factor: 2,
315
317
  time: 100
@@ -317,8 +319,8 @@ const defaultOptions = {
317
319
  function retrier(fn, options = defaultOptions) {
318
320
  let attempts = 0;
319
321
  function runAttempt(fn) {
320
- const defaultDelayTime = Math.round(options.time * Math.pow(options.factor, attempts));
321
- const retry = (ms) => delay(ms !== null && ms !== void 0 ? ms : defaultDelayTime).then(() => {
322
+ const defaultDelayTime = Math.round(options.time * options.factor ** attempts);
323
+ const retry = (ms) => delay(ms ?? defaultDelayTime).then(() => {
322
324
  attempts += 1;
323
325
  return runAttempt(fn);
324
326
  });
@@ -341,7 +343,7 @@ function getTimeoutFromThrottledRequest(error) {
341
343
  function retryIfThrottled(fn, retryThrottledMaxTimes) {
342
344
  return retrier(({ attempt, retry }) => fn().catch((error) => {
343
345
  if ('response' in error &&
344
- (error === null || error === void 0 ? void 0 : error.code) === REQUEST_WAS_THROTTLED_CODE &&
346
+ error?.code === REQUEST_WAS_THROTTLED_CODE &&
345
347
  attempt < retryThrottledMaxTimes) {
346
348
  return retry(getTimeoutFromThrottledRequest(error));
347
349
  }
@@ -358,41 +360,38 @@ function getStoreValue(store) {
358
360
  * Can be canceled and has progress.
359
361
  */
360
362
  function base(file, { publicKey, fileName, contentType, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
361
- return retryIfThrottled(() => {
362
- var _a;
363
- return request({
364
- method: 'POST',
365
- url: getUrl(baseURL, '/base/', {
366
- jsonerrors: 1
367
- }),
368
- headers: {
369
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
363
+ return retryIfThrottled(() => request({
364
+ method: 'POST',
365
+ url: getUrl(baseURL, '/base/', {
366
+ jsonerrors: 1
367
+ }),
368
+ headers: {
369
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
370
+ },
371
+ data: buildFormData({
372
+ file: {
373
+ data: file,
374
+ name: fileName ?? file.name ?? defaultFilename,
375
+ contentType
370
376
  },
371
- data: buildFormData({
372
- file: {
373
- data: file,
374
- name: (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename,
375
- contentType
376
- },
377
- UPLOADCARE_PUB_KEY: publicKey,
378
- UPLOADCARE_STORE: getStoreValue(store),
379
- signature: secureSignature,
380
- expire: secureExpire,
381
- source: source,
382
- metadata
383
- }),
384
- signal,
385
- onProgress
386
- }).then(({ data, headers, request }) => {
387
- const response = camelizeKeys(JSON.parse(data));
388
- if ('error' in response) {
389
- throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
390
- }
391
- else {
392
- return response;
393
- }
394
- });
395
- }, retryThrottledRequestMaxTimes);
377
+ UPLOADCARE_PUB_KEY: publicKey,
378
+ UPLOADCARE_STORE: getStoreValue(store),
379
+ signature: secureSignature,
380
+ expire: secureExpire,
381
+ source: source,
382
+ metadata
383
+ }),
384
+ signal,
385
+ onProgress
386
+ }).then(({ data, headers, request }) => {
387
+ const response = camelizeKeys(JSON.parse(data));
388
+ if ('error' in response) {
389
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
390
+ }
391
+ else {
392
+ return response;
393
+ }
394
+ }), retryThrottledRequestMaxTimes);
396
395
  }
397
396
 
398
397
  var TypeEnum;
@@ -571,9 +570,9 @@ function multipartStart(size, { publicKey, contentType, fileName, multipartChunk
571
570
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
572
571
  },
573
572
  data: buildFormData({
574
- filename: fileName !== null && fileName !== void 0 ? fileName : defaultFilename,
573
+ filename: fileName ?? defaultFilename,
575
574
  size: size,
576
- content_type: contentType !== null && contentType !== void 0 ? contentType : defaultContentType,
575
+ content_type: contentType ?? defaultContentType,
577
576
  part_size: multipartChunkSize,
578
577
  UPLOADCARE_STORE: getStoreValue(store),
579
578
  UPLOADCARE_PUB_KEY: publicKey,
@@ -805,8 +804,7 @@ class Events {
805
804
  this.events = Object.create({});
806
805
  }
807
806
  emit(event, data) {
808
- var _a;
809
- (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.forEach((fn) => fn(data));
807
+ this.events[event]?.forEach((fn) => fn(data));
810
808
  }
811
809
  on(event, callback) {
812
810
  this.events[event] = this.events[event] || [];
@@ -824,12 +822,12 @@ class Events {
824
822
 
825
823
  const response = (type, data) => {
826
824
  if (type === 'success') {
827
- return Object.assign({ status: Status.Success }, data);
825
+ return { status: Status.Success, ...data };
828
826
  }
829
827
  if (type === 'progress') {
830
- return Object.assign({ status: Status.Progress }, data);
828
+ return { status: Status.Progress, ...data };
831
829
  }
832
- return Object.assign({ status: Status.Error }, data);
830
+ return { status: Status.Error, ...data };
833
831
  };
834
832
  class Pusher {
835
833
  constructor(pusherKey, disconnectTime = 30000) {
@@ -877,8 +875,7 @@ class Pusher {
877
875
  }
878
876
  disconnect() {
879
877
  const actualDisconect = () => {
880
- var _a;
881
- (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
878
+ this.ws?.close();
882
879
  this.ws = undefined;
883
880
  this.isConnected = false;
884
881
  };
@@ -892,9 +889,8 @@ class Pusher {
892
889
  }
893
890
  }
894
891
  send(event, data) {
895
- var _a;
896
892
  const str = JSON.stringify({ event, data });
897
- (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(str);
893
+ this.ws?.send(str);
898
894
  }
899
895
  subscribe(token, handler) {
900
896
  this.subscribers += 1;
@@ -1061,7 +1057,7 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
1061
1057
  }))
1062
1058
  .catch((error) => {
1063
1059
  const pusher = getPusher(pusherKey);
1064
- pusher === null || pusher === void 0 ? void 0 : pusher.disconnect();
1060
+ pusher?.disconnect();
1065
1061
  return Promise.reject(error);
1066
1062
  })
1067
1063
  .then((urlResponse) => {
@@ -1234,7 +1230,7 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1234
1230
  return multipartStart(size, {
1235
1231
  publicKey,
1236
1232
  contentType,
1237
- fileName: fileName !== null && fileName !== void 0 ? fileName : file.name,
1233
+ fileName: fileName ?? file.name,
1238
1234
  baseURL,
1239
1235
  secureSignature,
1240
1236
  secureExpire,
@@ -1486,7 +1482,10 @@ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.
1486
1482
  /**
1487
1483
  * Populate options with settings.
1488
1484
  */
1489
- const populateOptionsWithSettings = (options, settings) => (Object.assign(Object.assign({}, settings), options));
1485
+ const populateOptionsWithSettings = (options, settings) => ({
1486
+ ...settings,
1487
+ ...options
1488
+ });
1490
1489
  class UploadClient {
1491
1490
  constructor(settings) {
1492
1491
  this.settings = Object.assign({}, defaultSettings, settings);
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Generated by dts-bundle-generator v6.9.0
1
+ // Generated by dts-bundle-generator v6.12.0
2
2
 
3
3
  import NodeFormData from 'form-data';
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uploadcare/upload-client",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "Library for work with Uploadcare Upload API",
5
5
  "type": "module",
6
6
  "main": "dist/index.node.js",
@@ -9,22 +9,21 @@
9
9
  "react-native": "dist/index.react-native.js",
10
10
  "types": "dist/types.d.ts",
11
11
  "sideEffects": false,
12
- "files": [
13
- "dist/*"
14
- ],
12
+ "files": ["dist/*"],
13
+ "engines": {
14
+ "node": ">=16"
15
+ },
15
16
  "scripts": {
16
- "check-env-vars": "node ./checkvars.js",
17
+ "check-env-vars": "node ../../checkvars.js",
17
18
  "mock:start": "node --loader ts-node/esm ./mock-server/server.ts --silent",
18
19
  "clean": "rimraf dist",
19
- "lint": "eslint ./src ./mock-server --ext=ts",
20
20
  "test": "start-server-and-test mock:start :3000 test:jest",
21
- "test:production": "npm run check-env-vars && TEST_ENV=production jest",
22
- "test:jest": "jest",
21
+ "test:production": "npm run check-env-vars && TEST_ENV=production npm run test:jest",
22
+ "test:jest": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js",
23
23
  "prebuild": "npm run clean",
24
24
  "build": "npm run build:types && npm run build:compile",
25
25
  "build:types": "dts-bundle-generator --project tsconfig.dts.json -o dist/types.d.ts src/index.ts",
26
- "build:compile": "rollup -c",
27
- "release": "shipjs prepare"
26
+ "build:compile": "rollup -c"
28
27
  },
29
28
  "repository": {
30
29
  "type": "git",
@@ -48,43 +47,20 @@
48
47
  "devDependencies": {
49
48
  "@koa/cors": "3.3.0",
50
49
  "@koa/router": "10.1.1",
51
- "@rollup/plugin-alias": "^3.1.9",
52
- "@rollup/plugin-node-resolve": "^13.3.0",
53
- "@rollup/plugin-typescript": "^8.3.2",
54
50
  "@types/express-serve-static-core": "^4.17.28",
55
- "@types/form-data": "2.5.0",
56
- "@types/jest": "27.0.0",
57
51
  "@types/koa": "2.13.4",
58
- "@types/node": "17.0.38",
59
- "@types/promise": "7.1.30",
60
52
  "@types/ws": "8.5.3",
61
- "@typescript-eslint/eslint-plugin": "5.27.0",
62
- "@typescript-eslint/parser": "5.27.0",
63
- "chalk": "4.1.0",
64
53
  "data-uri-to-buffer": "3.0.1",
65
54
  "dataurl-to-blob": "0.0.1",
66
55
  "dotenv": "8.2.0",
67
- "dts-bundle-generator": "6.9.0",
68
- "eslint": "8.2.0",
69
- "eslint-config-prettier": "8.3.0",
70
- "eslint-plugin-prettier": "4.0.0",
71
- "jest": "^28.1.0",
72
56
  "jest-environment-jsdom": "28.1.0",
73
57
  "jest-websocket-mock": "2.3.0",
74
58
  "koa": "2.13.4",
75
59
  "koa-add-trailing-slashes": "2.0.1",
76
60
  "koa-body": "5.0.0",
77
61
  "mock-socket": "9.0.3",
78
- "prettier": "2.2.1",
79
- "prettier-config-standard": "4.0.0",
80
- "rimraf": "3.0.2",
81
- "rollup": "^2.75.5",
82
- "shipjs": "0.24.0",
83
62
  "start-server-and-test": "1.11.7",
84
- "ts-jest": "28.0.3",
85
- "ts-node": "^10.8.0",
86
- "tslib": "^2.4.0",
87
- "typescript": "^4.7.2"
63
+ "@uploadcare/api-client-utils": "^4.1.0"
88
64
  },
89
65
  "dependencies": {
90
66
  "form-data": "^4.0.0",
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2019 Uploadcare Inc.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.